diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..2e7bba5 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,34 @@ +JAVA version : Openjdk 17 (jdk-17.0.2) + +For launch.json: +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "FhsmsCApplication", + "request": "launch", + "mainClass": "com.ffii.fhsmsc.FhsmsCApplication", + "projectName": "FhsmsC" + }, + { + "type": "java", + "name": "Launch Local", + "request": "launch", + "mainClass": "com.ffii.fhsmsc.FhsmsCApplication", + "console": "internalConsole", + "projectName": "", + "args": "--spring.profiles.active=db-local,local" + } + ] +} + +######################### +For build war +1. run .\gradlew clean build +2. go to path: .\build\libs +3. copy war to Tomcat server + diff --git a/bin/main/application-db-anna.yml b/bin/main/application-db-anna.yml new file mode 100644 index 0000000..55fe7cd --- /dev/null +++ b/bin/main/application-db-anna.yml @@ -0,0 +1,11 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://192.168.1.228:3306/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + driver-class-name: com.mysql.cj.jdbc.Driver + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/bin/main/application-db-local.yml b/bin/main/application-db-local.yml new file mode 100644 index 0000000..220285b --- /dev/null +++ b/bin/main/application-db-local.yml @@ -0,0 +1,10 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://127.0.0.1:3308/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/bin/main/application-db-prod.yml b/bin/main/application-db-prod.yml new file mode 100644 index 0000000..a1509bf --- /dev/null +++ b/bin/main/application-db-prod.yml @@ -0,0 +1,11 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://127.0.0.1:3306/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + driver-class-name: com.mysql.cj.jdbc.Driver + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/bin/main/application-ldap-prod.yml b/bin/main/application-ldap-prod.yml new file mode 100644 index 0000000..6974913 --- /dev/null +++ b/bin/main/application-ldap-prod.yml @@ -0,0 +1,9 @@ +spring: + ldap: + embedded: + port: 8389 + base-dn: dc=springframework,dc=org + ldif: classpath:ldap-test-users.ldif + validation: + enabled: false + urls: ldap://localhost:8389 \ No newline at end of file diff --git a/bin/main/application-local.yml b/bin/main/application-local.yml new file mode 100644 index 0000000..5997a49 --- /dev/null +++ b/bin/main/application-local.yml @@ -0,0 +1,2 @@ +host: + url: http://localhost:3000 \ No newline at end of file diff --git a/bin/main/application-prod-linux.yml b/bin/main/application-prod-linux.yml new file mode 100644 index 0000000..41ac797 --- /dev/null +++ b/bin/main/application-prod-linux.yml @@ -0,0 +1,2 @@ +logging: + config: 'classpath:log4j2-prod-linux.yml' \ No newline at end of file diff --git a/bin/main/application-prod-win.yml b/bin/main/application-prod-win.yml new file mode 100644 index 0000000..b1a50a9 --- /dev/null +++ b/bin/main/application-prod-win.yml @@ -0,0 +1,4 @@ +server: + port: 8087 +logging: + config: 'classpath:log4j2-prod-win.yml' \ No newline at end of file diff --git a/bin/main/application.yml b/bin/main/application.yml new file mode 100644 index 0000000..70f858c --- /dev/null +++ b/bin/main/application.yml @@ -0,0 +1,28 @@ +server: + servlet: + contextPath: /api + encoding: + charset: UTF-8 + enabled: true + force: true + port: 8090 + error: + include-message: always + tomcat: + connection-timeout: 300000 +spring: + servlet: + multipart: + max-file-size: 500MB + max-request-size: 600MB + jpa: + hibernate: + naming: + physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + database-platform: org.hibernate.dialect.MySQL8Dialect + properties: + hibernate: + dialect: + storage_engine: innodb +logging: + config: 'classpath:log4j2.yml' \ No newline at end of file diff --git a/bin/main/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ b/bin/main/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ new file mode 100644 index 0000000..6c50f02 --- /dev/null +++ b/bin/main/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ @@ -0,0 +1 @@ +C:\workspace\FHSMS-C-backend\src\main\java\com\ffii\fhsmsc\modules\Menu\req\fb_menuResponsity.java \ No newline at end of file diff --git a/bin/main/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ b/bin/main/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ new file mode 100644 index 0000000..d98a001 --- /dev/null +++ b/bin/main/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ @@ -0,0 +1 @@ +C:\workspace\FHSMS-C-backend\src\main\java\com\ffii\fhsmsc\modules\User_group\enity\UserGroupMembersRepository.java \ No newline at end of file diff --git a/bin/main/db/changelog/changes/00_system_base/01_create_table.sql b/bin/main/db/changelog/changes/00_system_base/01_create_table.sql new file mode 100644 index 0000000..cc658c8 --- /dev/null +++ b/bin/main/db/changelog/changes/00_system_base/01_create_table.sql @@ -0,0 +1,385 @@ +--liquibase formatted sql + +--changeset terence:create table +--comment: create table + +-- +-- Table structure for table `audit_log` +-- + +DROP TABLE IF EXISTS `audit_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `audit_log` ( + `tableName` varchar(30) NOT NULL, + `recordId` int NOT NULL, + `recordName` varchar(500) DEFAULT NULL, + `modifiedBy` int DEFAULT NULL, + `modified` datetime DEFAULT NULL, + `oldData` json DEFAULT NULL, + `newData` json DEFAULT NULL, + KEY `idx_tableName_recordId` (`tableName`,`recordId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `audit_log` +-- + +LOCK TABLES `audit_log` WRITE; +/*!40000 ALTER TABLE `audit_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `audit_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `authority` +-- + +DROP TABLE IF EXISTS `authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(255) NOT NULL, + `name` varchar(100) NOT NULL, + `module` varchar(50) DEFAULT NULL, + `description` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `authority` +-- + +LOCK TABLES `authority` WRITE; +/*!40000 ALTER TABLE `authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file` +-- + +DROP TABLE IF EXISTS `file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `filename` varchar(255) NOT NULL, + `skey` varchar(50) NOT NULL, + `extension` varchar(10) DEFAULT NULL, + `mimetype` varchar(100) DEFAULT NULL, + `filesize` int DEFAULT NULL, + `remarks` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file` +-- + +LOCK TABLES `file` WRITE; +/*!40000 ALTER TABLE `file` DISABLE KEYS */; +/*!40000 ALTER TABLE `file` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file_blob` +-- + +DROP TABLE IF EXISTS `file_blob`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file_blob` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `fileId` int NOT NULL, + `bytes` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file_blob` +-- + +LOCK TABLES `file_blob` WRITE; +/*!40000 ALTER TABLE `file_blob` DISABLE KEYS */; +/*!40000 ALTER TABLE `file_blob` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file_ref` +-- + +DROP TABLE IF EXISTS `file_ref`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file_ref` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `fileId` int DEFAULT NULL, + `refCode` varchar(50) DEFAULT NULL, + `refId` int NOT NULL, + `refType` varchar(50) NOT NULL, + `remarks` varchar(255) DEFAULT NULL, + `thumbnailFileId` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file_ref` +-- + +LOCK TABLES `file_ref` WRITE; +/*!40000 ALTER TABLE `file_ref` DISABLE KEYS */; +/*!40000 ALTER TABLE `file_ref` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group` +-- + +DROP TABLE IF EXISTS `group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `group` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `name` varchar(50) NOT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group` +-- + +LOCK TABLES `group` WRITE; +/*!40000 ALTER TABLE `group` DISABLE KEYS */; +/*!40000 ALTER TABLE `group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_authority` +-- + +DROP TABLE IF EXISTS `group_authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `group_authority` ( + `groupId` int NOT NULL, + `authId` int NOT NULL, + PRIMARY KEY (`groupId`,`authId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group_authority` +-- + +LOCK TABLES `group_authority` WRITE; +/*!40000 ALTER TABLE `group_authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `group_authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `settings` +-- + +DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `settings` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `value` text NOT NULL, + `category` varchar(50) DEFAULT NULL, + `type` varchar(10) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `name_idx` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `settings` +-- + +LOCK TABLES `settings` WRITE; +/*!40000 ALTER TABLE `settings` DISABLE KEYS */; +/*!40000 ALTER TABLE `settings` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `username` varchar(30) NOT NULL, + `password` varchar(60) DEFAULT NULL, + `locked` tinyint(1) NOT NULL DEFAULT '0', + `expiryDate` date DEFAULT NULL, + `name` varchar(50) NOT NULL, + `locale` varchar(5) DEFAULT NULL, + `fullname` varchar(90) DEFAULT NULL, + `firstname` varchar(45) DEFAULT NULL, + `lastname` varchar(30) DEFAULT NULL, + `title` varchar(60) DEFAULT NULL, + `department` varchar(60) DEFAULT NULL, + `email` varchar(120) DEFAULT NULL, + `phone1` varchar(30) DEFAULT NULL, + `phone2` varchar(30) DEFAULT NULL, + `remarks` varchar(600) DEFAULT NULL, + `post` varchar(50) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user` +-- + +LOCK TABLES `user` WRITE; +/*!40000 ALTER TABLE `user` DISABLE KEYS */; +/*!40000 ALTER TABLE `user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_authority` +-- + +DROP TABLE IF EXISTS `user_authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_authority` ( + `userId` int NOT NULL, + `authId` int NOT NULL, + PRIMARY KEY (`userId`,`authId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_authority` +-- + +LOCK TABLES `user_authority` WRITE; +/*!40000 ALTER TABLE `user_authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_group` +-- + +DROP TABLE IF EXISTS `user_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_group` ( + `groupId` int NOT NULL, + `userId` int NOT NULL, + PRIMARY KEY (`groupId`,`userId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_group` +-- + +LOCK TABLES `user_group` WRITE; +/*!40000 ALTER TABLE `user_group` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_login_log` +-- + +DROP TABLE IF EXISTS `user_login_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_login_log` ( + `username` varchar(32) NOT NULL, + `loginTime` datetime NOT NULL, + `ipAddr` varchar(45) NOT NULL, + `success` tinyint(1) NOT NULL, + PRIMARY KEY (`username`,`loginTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_login_log` +-- + +LOCK TABLES `user_login_log` WRITE; +/*!40000 ALTER TABLE `user_login_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_login_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_password_history` +-- + +DROP TABLE IF EXISTS `user_password_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_password_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `userId` int NOT NULL, + `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `password` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_password_history` +-- + +LOCK TABLES `user_password_history` WRITE; +/*!40000 ALTER TABLE `user_password_history` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_password_history` ENABLE KEYS */; +UNLOCK TABLES; + diff --git a/bin/main/db/changelog/changes/00_system_base/02_insert_group_authority.sql b/bin/main/db/changelog/changes/00_system_base/02_insert_group_authority.sql new file mode 100644 index 0000000..3da0f9d --- /dev/null +++ b/bin/main/db/changelog/changes/00_system_base/02_insert_group_authority.sql @@ -0,0 +1,32 @@ +--liquibase formatted sql + +--changeset terence:insert group authority +--comment: insert group authority + +LOCK TABLES `authority` WRITE; +/*!40000 ALTER TABLE `authority` DISABLE KEYS */; +INSERT INTO `authority` VALUES (1,'MAINTAIN_USER_GROUP','Maintain User Group','System Administration','Allow to maintain user group'),(2,'MAINTAIN_USER','Maintain User','System Administration','Allow to maintain user'),(3,'VIEW_AUDIT_LOG','View Audit Log','System Administration','Allow to view audit log'),(4,'VIEW_LOGIN_LOG','View Login Log','System Administration','Allow to view login log'),(5,'MANAGE_PASSWORD_POLICY','Manage Password Policy','System Administration','Allow to manage password policy'),(6,'MANAGE_SYSTEM_CONFIGURATION','Manage System Configuration','System Administration','Allow to manage system configuration'); +/*!40000 ALTER TABLE `authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group` +-- + +LOCK TABLES `group` WRITE; +/*!40000 ALTER TABLE `group` DISABLE KEYS */; +INSERT INTO `group` VALUES + (1,NOW(),1,0,NOW(),1,0,'System Administrator','System Administrator Group') + ; +/*!40000 ALTER TABLE `group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_authority` +-- + +LOCK TABLES `group_authority` WRITE; +/*!40000 ALTER TABLE `group_authority` DISABLE KEYS */; +INSERT INTO `group_authority` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6); +/*!40000 ALTER TABLE `group_authority` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/bin/main/db/changelog/changes/00_system_base/03_insert_setting.sql b/bin/main/db/changelog/changes/00_system_base/03_insert_setting.sql new file mode 100644 index 0000000..8264c84 --- /dev/null +++ b/bin/main/db/changelog/changes/00_system_base/03_insert_setting.sql @@ -0,0 +1,10 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +LOCK TABLES `settings` WRITE; +/*!40000 ALTER TABLE `settings` DISABLE KEYS */; +INSERT INTO `settings` VALUES (1,'SYS.idleLogoutTime','60','settings','integer'),(2,'MAIL.smtp.host','','settings','string'),(3,'MAIL.smtp.port','25','settings','integer'),(4,'MAIL.smtp.username','','settings','string'),(5,'MAIL.smtp.password','','settings','string'),(6,'MAIL.smtp.auth','','settings','boolean'),(7,'SYS.password.rule.length.min','10','passwordPolicy','integer'),(8,'SYS.password.rule.duration','180','passwordPolicy','integer'),(9,'SYS.password.rule.history','8','passwordPolicy','integer'),(10,'SYS.loginAttempt.limit','5','passwordPolicy','integer'),(11,'SYS.loginAttempt.penalityTime','5','passwordPolicy','integer'),(12,'SYS.password.rule.numberAndAlphabetic','true','passwordPolicy','boolean'),(13,'SYS.password.rule.specialCharacter','true','passwordPolicy','boolean'),(14,'SYS.password.rule.notContainUsername','true','passwordPolicy','boolean'),(15,'SYS.password.rule.notContainThreeConsecutiveCharacters','true','passwordPolicy','boolean'); +/*!40000 ALTER TABLE `settings` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/bin/main/db/changelog/changes/00_system_base/04_create_user(optional).sql b/bin/main/db/changelog/changes/00_system_base/04_create_user(optional).sql new file mode 100644 index 0000000..9268e08 --- /dev/null +++ b/bin/main/db/changelog/changes/00_system_base/04_create_user(optional).sql @@ -0,0 +1,8 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +INSERT INTO `user` ( `id`, `created`, `createdBy`, `version`, `modified`, `modifiedBy`, `deleted`, `username`, `password`, `locked`, `expiryDate`, `name`, `locale`, `fullname`, `firstname`, `lastname`, `title`, `department`, `email`, `phone1`, `phone2`, `remarks`, `post` ) VALUES ( '1', NOW(), '1', '0', NOW(), '1', '0', '2fi', '$2a$10$cRJ.TOZPExg49qk.zmJlruW/Rj7X05vVMxD1CgAOOegZMsC1IPCFm', '0', NULL, '2fi', NULL, '2fi', NULL, NULL, NULL, NULL, '', '', NULL, NULL, '' ); + +INSERT INTO `user_group` (`groupId`, `userId`) VALUES ('1', '1'); diff --git a/bin/main/db/changelog/changes/enson_change/04_create_user(enson).sql b/bin/main/db/changelog/changes/enson_change/04_create_user(enson).sql new file mode 100644 index 0000000..dbed519 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/04_create_user(enson).sql @@ -0,0 +1,8 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +INSERT INTO `user` ( `id`, `created`, `createdBy`, `version`, `modified`, `modifiedBy`, `deleted`, `username`, `password`, `locked`, `expiryDate`, `name`, `locale`, `fullname`, `firstname`, `lastname`, `title`, `department`, `email`, `phone1`, `phone2`, `remarks`, `post` ) VALUES ( '2', NOW(), '2', '0', NOW(), '2', '0', '2fi2', '1234', '0', NULL, '2fi2', NULL, '2fi2', NULL, NULL, NULL, NULL, '', '', NULL, NULL, '' ); + +INSERT INTO `user_group` (`groupId`, `userId`) VALUES ('2', '2'); diff --git a/bin/main/db/changelog/changes/enson_change/05_create_food1.sql b/bin/main/db/changelog/changes/enson_change/05_create_food1.sql new file mode 100644 index 0000000..aed6f3e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/05_create_food1.sql @@ -0,0 +1,54 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `food1` ( + `id` int(11) NOT NULL, + `name` varchar(100) NOT NULL, + `kcal` decimal(10,2) DEFAULT NULL, + `protein` decimal(10,2) NOT NULL DEFAULT 0.00, + `fat` decimal(10,2) NOT NULL DEFAULT 0.00, + `satFat` decimal(10,2) NOT NULL DEFAULT 0.00, + `carbo` decimal(10,2) NOT NULL DEFAULT 0.00, + `sodium` decimal(10,2) NOT NULL DEFAULT 0.00, + `Ingredients` varchar(255) DEFAULT NULL, + `food_type` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +INSERT INTO `food1` (`id`, `name`, `kcal`, `protein`, `fat`, `satFat`, `carbo`, `sodium`, `Ingredients`, `food_type`) VALUES +(1, 'Apple', 52.00, 0.00, 0.00, 0.00, 14.00, 0.00, NULL, 'fruit'), +(2, 'Banana', 105.00, 1.30, 0.30, 0.10, 27.00, 1.00, NULL, 'fruit'), +(3, 'Chicken breast', 165.00, 31.00, 6.10, 2.00, 1.00, 69.00, NULL, 'food'), +(4, 'Brown rice', 216.00, 5.00, 3.00, 0.40, 45.00, 0.00, NULL, 'food'), +(5, 'Yogurt', 148.00, 5.20, 6.10, 3.40, 10.00, 125.00, NULL, 'food'), +(6, 'Egg', 78.00, 6.30, 5.60, 1.60, 0.40, 62.00, NULL, 'food'), +(7, 'Avocado', 160.00, 2.00, 14.60, 2.00, 8.50, 7.00, NULL, 'fruit'), +(8, 'Salmon', 206.00, 22.00, 13.60, 3.80, 5.00, 0.00, NULL, 'food'), +(9, 'Spinach', 23.00, 2.70, 0.40, 0.10, 3.60, 79.00, NULL, 'vegetable'), +(10, 'Carrot', 41.00, 0.90, 0.20, 0.00, 9.60, 69.00, NULL, 'vegetable'), +(11, 'Broccoli', 34.00, 2.80, 0.60, 0.20, 6.60, 66.00, NULL, 'vegetable'), +(12, 'Potato', 77.00, 2.00, 0.10, 0.00, 17.30, 10.00, NULL, 'vegetable'), +(13, 'Sweet potato', 101.00, 1.60, 0.10, 0.00, 22.00, 3.00, NULL, 'vegetable'), +(14, 'Onion', 40.00, 1.40, 0.10, 0.00, 9.30, 3.00, NULL, 'vegetable'), +(15, 'Garlic', 140.00, 6.70, 15.40, 3.30, 6.20, 167.00, NULL, 'food'), +(16, 'Tomato', 18.00, 0.90, 0.20, 0.00, 3.90, 5.00, NULL, 'vegetable'), +(17, 'Cucumber', 16.00, 0.70, 0.10, 0.00, 3.80, 2.00, NULL, 'fruit'), +(18, 'Lettuce', 8.00, 0.80, 0.20, 0.10, 1.70, 5.00, NULL, 'vegetable'), +(19, 'Bread', 265.00, 9.40, 4.60, 1.30, 52.00, 150.00, NULL, 'food'), +(20, 'Pasta', 131.00, 5.50, 1.00, 0.20, 26.00, 0.00, NULL, 'food'), +(21, 'Cheese', 402.00, 20.60, 33.10, 23.20, 1.30, 0.00, NULL, 'food'), +(22, 'Milk', 50.00, 3.30, 3.30, 1.70, 4.80, 120.00, NULL, 'drink'), +(23, 'Beef', 250.00, 22.00, 21.00, 8.00, 0.00, 50.00, NULL, 'food'), +(24, 'Pork', 241.00, 15.10, 19.70, 7.30, 0.00, 49.00, NULL, 'food'), +(25, 'Lamb', 294.00, 17.50, 23.20, 9.20, 0.00, 0.00, NULL, 'food'), +(26, 'Chicken thigh', 197.00, 27.00, 14.90, 5.20, 0.00, 0.00, NULL, 'food'); + + +ALTER TABLE `food1` + ADD PRIMARY KEY (`id`); + +ALTER TABLE `food1` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=27; +COMMIT; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/06_create_food_information.sql b/bin/main/db/changelog/changes/enson_change/06_create_food_information.sql new file mode 100644 index 0000000..4420cb9 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/06_create_food_information.sql @@ -0,0 +1,19 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `food_items` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + `kcal` float DEFAULT NULL, + `protein` float DEFAULT NULL, + `fat` float DEFAULT NULL, + `sat_fat` float DEFAULT NULL, + `carbo` float DEFAULT NULL, + `sodium` float DEFAULT NULL, + `date` datetime DEFAULT NULL, + `size` float DEFAULT NULL, + `meal` varchar(50) DEFAULT NULL, + `user_id` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/07_alter_food_information.sql b/bin/main/db/changelog/changes/enson_change/07_alter_food_information.sql new file mode 100644 index 0000000..214f255 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/07_alter_food_information.sql @@ -0,0 +1,352 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Alter table and insert data + +INSERT INTO `food_items` (`id`, `name`, `kcal`, `protein`, `fat`, `sat_fat`, `carbo`, `sodium`, `date`, `size`, `meal`, `user_id`) VALUES +(4, 'Banana', 105, 1.3, 0.3, 0.1, 27, 1, '2024-11-12 03:36:53', 100, 'breakfast', 1), +(5, 'Banana', 105, 1.3, 0.3, 0.1, 27, 1, '2024-11-10 04:04:28', 100, 'breakfast', 2), +(7, 'Banana', 9450, 117, 27, 9, 2430, 90, '2024-11-11 14:20:39', 9000, 'breakfast', 1), +(16, 'Salmon', 206, 22, 13.6, 3.8, 5, 0, '2024-11-14 02:02:19', 100, 'Dinner', 1), +(21, 'Pork', 180.8, 11.3, 14.8, 5.5, 0, 36.8, '2024-11-04 06:09:00', 75, 'Breakfast', 1), +(22, 'Pork', 337.4, 21.1, 27.6, 10.2, 0, 68.6, '2024-11-04 13:06:00', 140, 'Lunch', 1), +(23, 'Bread', 532.6, 18.9, 9.2, 2.6, 104.5, 301.5, '2024-11-04 17:28:00', 201, 'Dinner', 1), +(24, 'Garlic', 436.8, 20.9, 48, 10.3, 19.3, 521, '2024-11-04 17:28:00', 312, 'Dinner', 1), +(25, 'Bread', 1240.2, 44, 21.5, 6.1, 243.4, 702, '2024-11-05 06:40:00', 468, 'Breakfast', 1), +(26, 'Cucumber', 26.4, 1.2, 0.2, 0, 6.3, 3.3, '2024-11-05 06:40:00', 165, 'Breakfast', 1), +(27, 'Apple', 86.8, 0, 0, 0, 23.4, 0, '2024-11-05 12:35:00', 167, 'Lunch', 1), +(28, 'Bread', 638.7, 22.7, 11.1, 3.1, 125.3, 361.5, '2024-11-05 12:35:00', 241, 'Lunch', 1), +(29, 'Milk', 63.5, 4.2, 4.2, 2.2, 6.1, 152.4, '2024-11-05 17:36:00', 127, 'Dinner', 1), +(30, 'Broccoli', 31.6, 2.6, 0.6, 0.2, 6.1, 61.4, '2024-11-06 08:02:00', 93, 'Breakfast', 1), +(31, 'Potato', 184, 4.8, 0.2, 0, 41.3, 23.9, '2024-11-06 11:54:00', 239, 'Lunch', 1), +(32, 'Brown rice', 395.3, 9.2, 5.5, 0.7, 82.4, 0, '2024-11-06 11:54:00', 183, 'Lunch', 1), +(33, 'Yogurt', 130.2, 4.6, 5.4, 3, 8.8, 110, '2024-11-06 17:23:00', 88, 'Dinner', 1), +(34, 'Milk', 235, 15.5, 15.5, 8, 22.6, 564, '2024-11-07 06:00:00', 470, 'Breakfast', 1), +(35, 'Pasta', 279, 11.7, 2.1, 0.4, 55.4, 0, '2024-11-07 06:00:00', 213, 'Breakfast', 1), +(36, 'Lamb', 385.1, 22.9, 30.4, 12.1, 0, 0, '2024-11-07 13:43:00', 131, 'Lunch', 1), +(37, 'Yogurt', 497.3, 17.5, 20.5, 11.4, 33.6, 420, '2024-11-07 13:43:00', 336, 'Lunch', 1), +(38, 'Egg', 122.5, 9.9, 8.8, 2.5, 0.6, 97.3, '2024-11-07 19:13:00', 157, 'Dinner', 1), +(39, 'Carrot', 89, 2, 0.4, 0, 20.8, 149.7, '2024-11-07 19:13:00', 217, 'Dinner', 1), +(40, 'Garlic', 680.4, 32.6, 74.8, 16, 30.1, 811.6, '2024-11-08 08:52:00', 486, 'Breakfast', 1), +(41, 'Egg', 79.6, 6.4, 5.7, 1.6, 0.4, 63.2, '2024-11-08 08:52:00', 102, 'Breakfast', 1), +(42, 'Potato', 63.9, 1.7, 0.1, 0, 14.4, 8.3, '2024-11-08 13:22:00', 83, 'Lunch', 1), +(43, 'Salmon', 984.7, 105.2, 65, 18.2, 23.9, 0, '2024-11-08 19:18:00', 478, 'Dinner', 1), +(44, 'Tomato', 86, 4.3, 1, 0, 18.6, 23.9, '2024-11-09 06:19:00', 478, 'Breakfast', 1), +(45, 'Garlic', 518, 24.8, 57, 12.2, 22.9, 617.9, '2024-11-09 11:32:00', 370, 'Lunch', 1), +(46, 'Beef', 907.5, 79.9, 76.2, 29, 0, 181.5, '2024-11-09 11:32:00', 363, 'Lunch', 1), +(47, 'Beef', 517.5, 45.5, 43.5, 16.6, 0, 103.5, '2024-11-09 18:54:00', 207, 'Dinner', 1), +(48, 'Brown rice', 658.8, 15.2, 9.1, 1.2, 137.2, 0, '2024-11-09 18:54:00', 305, 'Dinner', 1), +(49, 'Egg', 69.4, 5.6, 5, 1.4, 0.4, 55.2, '2024-11-10 08:40:00', 89, 'Breakfast', 1), +(50, 'Apple', 90, 0, 0, 0, 24.2, 0, '2024-11-10 11:17:00', 173, 'Lunch', 1), +(51, 'Chicken breast', 227.7, 42.8, 8.4, 2.8, 0, 95.2, '2024-11-10 11:17:00', 138, 'Lunch', 1), +(52, 'Brown rice', 561.6, 13, 7.8, 1, 117, 0, '2024-11-10 17:05:00', 260, 'Dinner', 1), +(53, 'Spinach', 85.3, 10, 1.5, 0.4, 13.4, 293.1, '2024-11-04 08:47:00', 371, 'Breakfast', 2), +(54, 'Potato', 224.8, 5.8, 0.3, 0, 50.5, 29.2, '2024-11-04 08:47:00', 292, 'Breakfast', 2), +(55, 'Pork', 282, 17.7, 23, 8.5, 0, 57.3, '2024-11-04 13:48:00', 117, 'Lunch', 2), +(56, 'Cheese', 695.5, 35.6, 57.3, 40.1, 2.2, 0, '2024-11-04 19:05:00', 173, 'Dinner', 2), +(57, 'Cucumber', 16, 0.7, 0.1, 0, 3.8, 2, '2024-11-05 07:37:00', 100, 'Breakfast', 2), +(58, 'Cheese', 1523.6, 78.1, 125.4, 87.9, 4.9, 0, '2024-11-05 07:37:00', 379, 'Breakfast', 2), +(59, 'Chicken thigh', 374.3, 51.3, 28.3, 9.9, 0, 0, '2024-11-05 13:53:00', 190, 'Lunch', 2), +(60, 'Chicken thigh', 439.3, 60.2, 33.2, 11.6, 0, 0, '2024-11-05 13:53:00', 223, 'Lunch', 2), +(61, 'Garlic', 520.8, 24.9, 57.3, 12.3, 23.1, 621.2, '2024-11-05 18:09:00', 372, 'Dinner', 2), +(62, 'Apple', 210.6, 0, 0, 0, 56.7, 0, '2024-11-05 18:09:00', 405, 'Dinner', 2), +(63, 'Pasta', 191.3, 8, 1.5, 0.3, 38, 0, '2024-11-06 08:19:00', 146, 'Breakfast', 2), +(64, 'Yogurt', 338.9, 11.9, 14, 7.8, 22.9, 286.2, '2024-11-06 13:41:00', 229, 'Lunch', 2), +(65, 'Cucumber', 41.9, 1.8, 0.3, 0, 10, 5.2, '2024-11-06 13:41:00', 262, 'Lunch', 2), +(66, 'Potato', 170.2, 4.4, 0.2, 0, 38.2, 22.1, '2024-11-06 17:15:00', 221, 'Dinner', 2), +(67, 'Carrot', 125.5, 2.8, 0.6, 0, 29.4, 211.1, '2024-11-06 17:15:00', 306, 'Dinner', 2), +(68, 'Chicken thigh', 496.4, 68, 37.5, 13.1, 0, 0, '2024-11-07 08:03:00', 252, 'Breakfast', 2), +(69, 'Egg', 59.3, 4.8, 4.3, 1.2, 0.3, 47.1, '2024-11-07 08:03:00', 76, 'Breakfast', 2), +(70, 'Onion', 117.2, 4.1, 0.3, 0, 27.2, 8.8, '2024-11-07 12:33:00', 293, 'Lunch', 2), +(71, 'Pasta', 119.2, 5, 0.9, 0.2, 23.7, 0, '2024-11-07 12:33:00', 91, 'Lunch', 2), +(72, 'Carrot', 81.2, 1.8, 0.4, 0, 19, 136.6, '2024-11-07 17:09:00', 198, 'Dinner', 2), +(73, 'Spinach', 70.4, 8.3, 1.2, 0.3, 11, 241.7, '2024-11-08 07:29:00', 306, 'Breakfast', 2), +(74, 'Bread', 1240.2, 44, 21.5, 6.1, 243.4, 702, '2024-11-08 07:29:00', 468, 'Breakfast', 2), +(75, 'Apple', 225.7, 0, 0, 0, 60.8, 0, '2024-11-08 13:24:00', 434, 'Lunch', 2), +(76, 'Garlic', 491.4, 23.5, 54.1, 11.6, 21.8, 586.2, '2024-11-08 13:24:00', 351, 'Lunch', 2), +(77, 'Chicken breast', 183.2, 34.4, 6.8, 2.2, 0, 76.6, '2024-11-08 18:06:00', 111, 'Dinner', 2), +(78, 'Banana', 487.2, 6, 1.4, 0.5, 125.3, 4.6, '2024-11-08 18:06:00', 464, 'Dinner', 2), +(79, 'Tomato', 21.2, 1.1, 0.2, 0, 4.6, 5.9, '2024-11-09 06:11:00', 118, 'Breakfast', 2), +(80, 'Spinach', 110.9, 13, 1.9, 0.5, 17.4, 380.8, '2024-11-09 06:11:00', 482, 'Breakfast', 2), +(81, 'Cucumber', 68, 3, 0.4, 0, 16.1, 8.5, '2024-11-09 12:33:00', 425, 'Lunch', 2), +(82, 'Chicken breast', 189.7, 35.6, 7, 2.3, 0, 79.3, '2024-11-09 17:53:00', 115, 'Dinner', 2), +(83, 'Lamb', 1396.5, 83.1, 110.2, 43.7, 0, 0, '2024-11-10 08:16:00', 475, 'Breakfast', 2), +(84, 'Lamb', 1437.7, 85.6, 113.4, 45, 0, 0, '2024-11-10 12:08:00', 489, 'Lunch', 2), +(85, 'Bread', 561.8, 19.9, 9.8, 2.8, 110.2, 318, '2024-11-10 12:08:00', 212, 'Lunch', 2), +(86, 'Chicken thigh', 478.7, 65.6, 36.2, 12.6, 0, 0, '2024-11-10 18:47:00', 243, 'Dinner', 2), +(87, 'Apple', 43.2, 0, 0, 0, 11.6, 0, '2024-11-04 08:13:00', 83, 'Breakfast', 3), +(88, 'Garlic', 109.2, 5.2, 12, 2.6, 4.8, 130.3, '2024-11-04 11:22:00', 78, 'Lunch', 3), +(89, 'Broccoli', 158.8, 13.1, 2.8, 0.9, 30.8, 308.2, '2024-11-04 11:22:00', 467, 'Lunch', 3), +(90, 'Milk', 200, 13.2, 13.2, 6.8, 19.2, 480, '2024-11-04 18:25:00', 400, 'Dinner', 3), +(91, 'Cheese', 1290.4, 66.1, 106.3, 74.5, 4.2, 0, '2024-11-05 07:51:00', 321, 'Breakfast', 3), +(92, 'Tomato', 71.6, 3.6, 0.8, 0, 15.5, 19.9, '2024-11-05 07:51:00', 398, 'Breakfast', 3), +(93, 'Lamb', 308.7, 18.4, 24.4, 9.7, 0, 0, '2024-11-05 13:43:00', 105, 'Lunch', 3), +(94, 'Yogurt', 464.7, 16.3, 19.2, 10.7, 31.4, 392.5, '2024-11-05 17:15:00', 314, 'Dinner', 3), +(95, 'Lamb', 726.2, 43.2, 57.3, 22.7, 0, 0, '2024-11-06 08:12:00', 247, 'Breakfast', 3), +(96, 'Beef', 607.5, 53.5, 51, 19.4, 0, 121.5, '2024-11-06 08:12:00', 243, 'Breakfast', 3), +(97, 'Garlic', 427, 20.4, 47, 10.1, 18.9, 509.3, '2024-11-06 11:43:00', 305, 'Lunch', 3), +(98, 'Salmon', 271.9, 29, 18, 5, 6.6, 0, '2024-11-06 19:18:00', 132, 'Dinner', 3), +(99, 'Salmon', 366.7, 39.2, 24.2, 6.8, 8.9, 0, '2024-11-07 07:44:00', 178, 'Breakfast', 3), +(100, 'Sweet potato', 404, 6.4, 0.4, 0, 88, 12, '2024-11-07 12:21:00', 400, 'Lunch', 3), +(101, 'Sweet potato', 101, 1.6, 0.1, 0, 22, 3, '2024-11-07 18:45:00', 100, 'Dinner', 3), +(102, 'Lettuce', 8.6, 0.9, 0.2, 0.1, 1.8, 5.4, '2024-11-08 08:42:00', 107, 'Breakfast', 3), +(103, 'Garlic', 233.8, 11.2, 25.7, 5.5, 10.4, 278.9, '2024-11-08 08:42:00', 167, 'Breakfast', 3), +(104, 'Milk', 207.5, 13.7, 13.7, 7.1, 19.9, 498, '2024-11-08 11:06:00', 415, 'Lunch', 3), +(105, 'Cheese', 747.7, 38.3, 61.6, 43.2, 2.4, 0, '2024-11-08 11:06:00', 186, 'Lunch', 3), +(106, 'Salmon', 828.1, 88.4, 54.7, 15.3, 20.1, 0, '2024-11-08 19:08:00', 402, 'Dinner', 3), +(107, 'Yogurt', 216.1, 7.6, 8.9, 5, 14.6, 182.5, '2024-11-08 19:08:00', 146, 'Dinner', 3), +(108, 'Bread', 816.2, 29, 14.2, 4, 160.2, 462, '2024-11-09 06:17:00', 308, 'Breakfast', 3), +(109, 'Onion', 190.4, 6.7, 0.5, 0, 44.3, 14.3, '2024-11-09 13:04:00', 476, 'Lunch', 3), +(110, 'Banana', 280.3, 3.5, 0.8, 0.3, 72.1, 2.7, '2024-11-09 13:04:00', 267, 'Lunch', 3), +(111, 'Avocado', 574.4, 7.2, 52.4, 7.2, 30.5, 25.1, '2024-11-09 17:53:00', 359, 'Dinner', 3), +(112, 'Carrot', 41.4, 0.9, 0.2, 0, 9.7, 69.7, '2024-11-10 06:57:00', 101, 'Breakfast', 3), +(113, 'Cheese', 1093.4, 56, 90, 63.1, 3.5, 0, '2024-11-10 11:51:00', 272, 'Lunch', 3), +(114, 'Pork', 253.1, 15.9, 20.7, 7.7, 0, 51.5, '2024-11-10 11:51:00', 105, 'Lunch', 3), +(115, 'Lettuce', 8.6, 0.9, 0.2, 0.1, 1.8, 5.4, '2024-11-10 17:02:00', 108, 'Dinner', 3), +(116, 'Milk', 98, 6.5, 6.5, 3.3, 9.4, 235.2, '2024-11-10 17:02:00', 196, 'Dinner', 3), +(117, 'Cucumber', 69.3, 3, 0.4, 0, 16.5, 8.7, '2024-11-04 07:44:00', 433, 'Breakfast', 4), +(118, 'Pasta', 482.1, 20.2, 3.7, 0.7, 95.7, 0, '2024-11-04 07:44:00', 368, 'Breakfast', 4), +(119, 'Lamb', 358.7, 21.3, 28.3, 11.2, 0, 0, '2024-11-04 11:37:00', 122, 'Lunch', 4), +(120, 'Pork', 257.9, 16.2, 21.1, 7.8, 0, 52.4, '2024-11-04 11:37:00', 107, 'Lunch', 4), +(121, 'Banana', 269.8, 3.3, 0.8, 0.3, 69.4, 2.6, '2024-11-04 18:32:00', 257, 'Dinner', 4), +(122, 'Tomato', 24.7, 1.2, 0.3, 0, 5.3, 6.9, '2024-11-04 18:32:00', 137, 'Dinner', 4), +(123, 'Yogurt', 384.8, 13.5, 15.9, 8.8, 26, 325, '2024-11-05 06:05:00', 260, 'Breakfast', 4), +(124, 'Yogurt', 380.4, 13.4, 15.7, 8.7, 25.7, 321.2, '2024-11-05 06:05:00', 257, 'Breakfast', 4), +(125, 'Lettuce', 17, 1.7, 0.4, 0.2, 3.6, 10.6, '2024-11-05 12:17:00', 212, 'Lunch', 4), +(126, 'Milk', 214, 14.1, 14.1, 7.3, 20.5, 513.6, '2024-11-05 12:17:00', 428, 'Lunch', 4), +(127, 'Egg', 244.9, 19.8, 17.6, 5, 1.3, 194.7, '2024-11-05 17:57:00', 314, 'Dinner', 4), +(128, 'Pork', 1185.7, 74.3, 96.9, 35.9, 0, 241.1, '2024-11-05 17:57:00', 492, 'Dinner', 4), +(129, 'Yogurt', 519.5, 18.3, 21.4, 11.9, 35.1, 438.8, '2024-11-06 06:03:00', 351, 'Breakfast', 4), +(130, 'Sweet potato', 502, 8, 0.5, 0, 109.3, 14.9, '2024-11-06 06:03:00', 497, 'Breakfast', 4), +(131, 'Bread', 1017.6, 36.1, 17.7, 5, 199.7, 576, '2024-11-06 12:56:00', 384, 'Lunch', 4), +(132, 'Lamb', 1237.7, 73.7, 97.7, 38.7, 0, 0, '2024-11-06 18:02:00', 421, 'Dinner', 4), +(133, 'Avocado', 384, 4.8, 35, 4.8, 20.4, 16.8, '2024-11-07 07:48:00', 240, 'Breakfast', 4), +(134, 'Milk', 103.5, 6.8, 6.8, 3.5, 9.9, 248.4, '2024-11-07 07:48:00', 207, 'Breakfast', 4), +(135, 'Broccoli', 124.8, 10.3, 2.2, 0.7, 24.2, 242.2, '2024-11-07 13:38:00', 367, 'Lunch', 4), +(136, 'Cheese', 932.6, 47.8, 76.8, 53.8, 3, 0, '2024-11-07 13:38:00', 232, 'Lunch', 4), +(137, 'Lettuce', 19.2, 1.9, 0.5, 0.2, 4.1, 12, '2024-11-07 17:46:00', 240, 'Dinner', 4), +(138, 'Lamb', 655.6, 39, 51.7, 20.5, 0, 0, '2024-11-07 17:46:00', 223, 'Dinner', 4), +(139, 'Chicken thigh', 922, 126.4, 69.7, 24.3, 0, 0, '2024-11-08 06:52:00', 468, 'Breakfast', 4), +(140, 'Chicken thigh', 165.5, 22.7, 12.5, 4.4, 0, 0, '2024-11-08 06:52:00', 84, 'Breakfast', 4), +(141, 'Brown rice', 607, 14.1, 8.4, 1.1, 126.5, 0, '2024-11-08 11:38:00', 281, 'Lunch', 4), +(142, 'Yogurt', 627.5, 22, 25.9, 14.4, 42.4, 530, '2024-11-08 18:25:00', 424, 'Dinner', 4), +(143, 'Chicken breast', 94, 17.7, 3.5, 1.1, 0, 39.3, '2024-11-09 08:14:00', 57, 'Breakfast', 4), +(144, 'Cucumber', 64.2, 2.8, 0.4, 0, 15.2, 8, '2024-11-09 11:24:00', 401, 'Lunch', 4), +(145, 'Cucumber', 20.5, 0.9, 0.1, 0, 4.9, 2.6, '2024-11-09 19:34:00', 128, 'Dinner', 4), +(146, 'Yogurt', 569.8, 20, 23.5, 13.1, 38.5, 481.2, '2024-11-09 19:34:00', 385, 'Dinner', 4), +(147, 'Egg', 172.4, 13.9, 12.4, 3.5, 0.9, 137, '2024-11-10 06:41:00', 221, 'Breakfast', 4), +(148, 'Milk', 218.5, 14.4, 14.4, 7.4, 21, 524.4, '2024-11-10 12:55:00', 437, 'Lunch', 4), +(149, 'Brown rice', 712.8, 16.5, 9.9, 1.3, 148.5, 0, '2024-11-10 12:55:00', 330, 'Lunch', 4), +(150, 'Garlic', 324.8, 15.5, 35.7, 7.7, 14.4, 387.4, '2024-11-10 18:24:00', 232, 'Dinner', 4), +(151, 'Potato', 57.8, 1.5, 0.1, 0, 13, 7.5, '2024-11-10 18:24:00', 75, 'Dinner', 4), +(152, 'Potato', 60.8, 1.6, 0.1, 0, 13.7, 7.9, '2024-11-04 07:23:00', 79, 'Breakfast', 5), +(153, 'Tomato', 85, 4.2, 0.9, 0, 18.4, 23.6, '2024-11-04 07:23:00', 472, 'Breakfast', 5), +(154, 'Sweet potato', 484.8, 7.7, 0.5, 0, 105.6, 14.4, '2024-11-04 11:51:00', 480, 'Lunch', 5), +(155, 'Lamb', 820.3, 48.8, 64.7, 25.7, 0, 0, '2024-11-04 17:50:00', 279, 'Dinner', 5), +(156, 'Pork', 301.2, 18.9, 24.6, 9.1, 0, 61.2, '2024-11-05 06:15:00', 125, 'Breakfast', 5), +(157, 'Bread', 1057.4, 37.5, 18.4, 5.2, 207.5, 598.5, '2024-11-05 06:15:00', 399, 'Breakfast', 5), +(158, 'Tomato', 85.9, 4.3, 1, 0, 18.6, 23.8, '2024-11-05 11:50:00', 477, 'Lunch', 5), +(159, 'Pork', 819.4, 51.3, 67, 24.8, 0, 166.6, '2024-11-05 11:50:00', 340, 'Lunch', 5), +(160, 'Egg', 48.4, 3.9, 3.5, 1, 0.2, 38.4, '2024-11-05 18:51:00', 62, 'Dinner', 5), +(161, 'Cucumber', 58.1, 2.5, 0.4, 0, 13.8, 7.3, '2024-11-05 18:51:00', 363, 'Dinner', 5), +(162, 'Garlic', 642.6, 30.8, 70.7, 15.1, 28.5, 766.5, '2024-11-06 08:26:00', 459, 'Breakfast', 5), +(163, 'Lettuce', 11, 1.1, 0.3, 0.1, 2.3, 6.9, '2024-11-06 08:26:00', 137, 'Breakfast', 5), +(164, 'Garlic', 253.4, 12.1, 27.9, 6, 11.2, 302.3, '2024-11-06 11:43:00', 181, 'Lunch', 5), +(165, 'Yogurt', 389.2, 13.7, 16, 8.9, 26.3, 328.8, '2024-11-06 11:43:00', 263, 'Lunch', 5), +(166, 'Beef', 1125, 99, 94.5, 36, 0, 225, '2024-11-06 18:56:00', 450, 'Dinner', 5), +(167, 'Brown rice', 382.3, 8.8, 5.3, 0.7, 79.7, 0, '2024-11-07 07:09:00', 177, 'Breakfast', 5), +(168, 'Avocado', 540.8, 6.8, 49.3, 6.8, 28.7, 23.7, '2024-11-07 13:16:00', 338, 'Lunch', 5), +(169, 'Broccoli', 154.7, 12.7, 2.7, 0.9, 30, 300.3, '2024-11-07 13:16:00', 455, 'Lunch', 5), +(170, 'Apple', 215.8, 0, 0, 0, 58.1, 0, '2024-11-07 18:54:00', 415, 'Dinner', 5), +(171, 'Spinach', 50.1, 5.9, 0.9, 0.2, 7.8, 172.2, '2024-11-08 07:10:00', 218, 'Breakfast', 5), +(172, 'Broccoli', 24.1, 2, 0.4, 0.1, 4.7, 46.9, '2024-11-08 11:41:00', 71, 'Lunch', 5), +(173, 'Milk', 189.5, 12.5, 12.5, 6.4, 18.2, 454.8, '2024-11-08 17:13:00', 379, 'Dinner', 5), +(174, 'Cheese', 1893.4, 97, 155.9, 109.3, 6.1, 0, '2024-11-08 17:13:00', 471, 'Dinner', 5), +(175, 'Salmon', 253.4, 27.1, 16.7, 4.7, 6.2, 0, '2024-11-09 07:15:00', 123, 'Breakfast', 5), +(176, 'Lettuce', 34.6, 3.5, 0.9, 0.4, 7.3, 21.6, '2024-11-09 07:15:00', 432, 'Breakfast', 5), +(177, 'Lettuce', 7.4, 0.7, 0.2, 0.1, 1.6, 4.7, '2024-11-09 12:38:00', 93, 'Lunch', 5), +(178, 'Spinach', 57.5, 6.8, 1, 0.2, 9, 197.5, '2024-11-09 12:38:00', 250, 'Lunch', 5), +(179, 'Cucumber', 27, 1.2, 0.2, 0, 6.4, 3.4, '2024-11-09 17:59:00', 169, 'Dinner', 5), +(180, 'Egg', 81.1, 6.6, 5.8, 1.7, 0.4, 64.5, '2024-11-09 17:59:00', 104, 'Dinner', 5), +(181, 'Tomato', 45.9, 2.3, 0.5, 0, 9.9, 12.8, '2024-11-10 07:50:00', 255, 'Breakfast', 5), +(182, 'Tomato', 81.9, 4.1, 0.9, 0, 17.7, 22.8, '2024-11-10 12:02:00', 455, 'Lunch', 5), +(183, 'Salmon', 379, 40.5, 25, 7, 9.2, 0, '2024-11-10 18:02:00', 184, 'Dinner', 5), +(184, 'Lamb', 308.7, 18.4, 24.4, 9.7, 0, 0, '2024-11-04 07:46:00', 105, 'Breakfast', 6), +(185, 'Lamb', 787.9, 46.9, 62.2, 24.7, 0, 0, '2024-11-04 07:46:00', 268, 'Breakfast', 6), +(186, 'Tomato', 76, 3.8, 0.8, 0, 16.5, 21.1, '2024-11-04 12:48:00', 422, 'Lunch', 6), +(187, 'Potato', 370.4, 9.6, 0.5, 0, 83.2, 48.1, '2024-11-04 12:48:00', 481, 'Lunch', 6), +(188, 'Lettuce', 21, 2.1, 0.5, 0.3, 4.5, 13.1, '2024-11-04 18:25:00', 263, 'Dinner', 6), +(189, 'Avocado', 552, 6.9, 50.4, 6.9, 29.3, 24.2, '2024-11-05 06:11:00', 345, 'Breakfast', 6), +(190, 'Carrot', 62.7, 1.4, 0.3, 0, 14.7, 105.6, '2024-11-05 11:02:00', 153, 'Lunch', 6), +(191, 'Yogurt', 134.7, 4.7, 5.6, 3.1, 9.1, 113.8, '2024-11-05 11:02:00', 91, 'Lunch', 6), +(192, 'Tomato', 78.8, 3.9, 0.9, 0, 17.1, 21.9, '2024-11-05 19:27:00', 438, 'Dinner', 6), +(193, 'Beef', 340, 29.9, 28.6, 10.9, 0, 68, '2024-11-05 19:27:00', 136, 'Dinner', 6), +(194, 'Onion', 108.4, 3.8, 0.3, 0, 25.2, 8.1, '2024-11-06 06:42:00', 271, 'Breakfast', 6), +(195, 'Egg', 350.2, 28.3, 25.1, 7.2, 1.8, 278.4, '2024-11-06 13:46:00', 449, 'Lunch', 6), +(196, 'Sweet potato', 494.9, 7.8, 0.5, 0, 107.8, 14.7, '2024-11-06 17:34:00', 490, 'Dinner', 6), +(197, 'Tomato', 83.7, 4.2, 0.9, 0, 18.1, 23.2, '2024-11-06 17:34:00', 465, 'Dinner', 6), +(198, 'Brown rice', 110.2, 2.5, 1.5, 0.2, 22.9, 0, '2024-11-07 06:48:00', 51, 'Breakfast', 6), +(199, 'Pasta', 615.7, 25.9, 4.7, 0.9, 122.2, 0, '2024-11-07 06:48:00', 470, 'Breakfast', 6), +(200, 'Potato', 150.2, 3.9, 0.2, 0, 33.7, 19.5, '2024-11-07 13:42:00', 195, 'Lunch', 6), +(201, 'Brown rice', 708.5, 16.4, 9.8, 1.3, 147.6, 0, '2024-11-07 13:42:00', 328, 'Lunch', 6), +(202, 'Lettuce', 25.9, 2.6, 0.6, 0.3, 5.5, 16.2, '2024-11-07 19:44:00', 324, 'Dinner', 6), +(203, 'Brown rice', 490.3, 11.3, 6.8, 0.9, 102.2, 0, '2024-11-08 08:13:00', 227, 'Breakfast', 6), +(204, 'Banana', 113.4, 1.4, 0.3, 0.1, 29.2, 1.1, '2024-11-08 08:13:00', 108, 'Breakfast', 6), +(205, 'Banana', 300.3, 3.7, 0.9, 0.3, 77.2, 2.9, '2024-11-08 12:25:00', 286, 'Lunch', 6), +(206, 'Pork', 848.3, 53.2, 69.3, 25.7, 0, 172.5, '2024-11-08 18:46:00', 352, 'Dinner', 6), +(207, 'Cucumber', 75.5, 3.3, 0.5, 0, 17.9, 9.4, '2024-11-09 07:57:00', 472, 'Breakfast', 6), +(208, 'Sweet potato', 361.6, 5.7, 0.4, 0, 78.8, 10.7, '2024-11-09 12:22:00', 358, 'Lunch', 6), +(209, 'Lettuce', 8.3, 0.8, 0.2, 0.1, 1.8, 5.2, '2024-11-09 12:22:00', 104, 'Lunch', 6), +(210, 'Pasta', 200.4, 8.4, 1.5, 0.3, 39.8, 0, '2024-11-09 17:26:00', 153, 'Dinner', 6), +(211, 'Pasta', 117.9, 5, 0.9, 0.2, 23.4, 0, '2024-11-10 07:29:00', 90, 'Breakfast', 6), +(212, 'Brown rice', 738.7, 17.1, 10.3, 1.4, 153.9, 0, '2024-11-10 07:29:00', 342, 'Breakfast', 6), +(213, 'Onion', 55.6, 1.9, 0.1, 0, 12.9, 4.2, '2024-11-10 11:18:00', 139, 'Lunch', 6), +(214, 'Potato', 338.8, 8.8, 0.4, 0, 76.1, 44, '2024-11-10 18:58:00', 440, 'Dinner', 6), +(215, 'Bread', 339.2, 12, 5.9, 1.7, 66.6, 192, '2024-11-10 18:58:00', 128, 'Dinner', 6), +(216, 'Carrot', 145.5, 3.2, 0.7, 0, 34.1, 244.9, '2024-11-04 06:16:00', 355, 'Breakfast', 7), +(217, 'Bread', 667.8, 23.7, 11.6, 3.3, 131, 378, '2024-11-04 13:58:00', 252, 'Lunch', 7), +(218, 'Tomato', 39.4, 2, 0.4, 0, 8.5, 10.9, '2024-11-04 13:58:00', 219, 'Lunch', 7), +(219, 'Cheese', 1234.1, 63.2, 101.6, 71.2, 4, 0, '2024-11-04 18:34:00', 307, 'Dinner', 7), +(220, 'Chicken thigh', 933.8, 128, 70.6, 24.6, 0, 0, '2024-11-04 18:34:00', 474, 'Dinner', 7), +(221, 'Banana', 155.4, 1.9, 0.4, 0.1, 40, 1.5, '2024-11-05 07:24:00', 148, 'Breakfast', 7), +(222, 'Yogurt', 464.7, 16.3, 19.2, 10.7, 31.4, 392.5, '2024-11-05 11:29:00', 314, 'Lunch', 7), +(223, 'Beef', 367.5, 32.3, 30.9, 11.8, 0, 73.5, '2024-11-05 11:29:00', 147, 'Lunch', 7), +(224, 'Yogurt', 122.8, 4.3, 5.1, 2.8, 8.3, 103.8, '2024-11-05 17:20:00', 83, 'Dinner', 7), +(225, 'Apple', 144, 0, 0, 0, 38.8, 0, '2024-11-06 07:30:00', 277, 'Breakfast', 7), +(226, 'Beef', 840, 73.9, 70.6, 26.9, 0, 168, '2024-11-06 07:30:00', 336, 'Breakfast', 7), +(227, 'Garlic', 522.2, 25, 57.4, 12.3, 23.1, 622.9, '2024-11-06 12:35:00', 373, 'Lunch', 7), +(228, 'Chicken thigh', 823.5, 112.9, 62.3, 21.7, 0, 0, '2024-11-06 12:35:00', 418, 'Lunch', 7), +(229, 'Salmon', 115.4, 12.3, 7.6, 2.1, 2.8, 0, '2024-11-06 17:54:00', 56, 'Dinner', 7), +(230, 'Tomato', 37.1, 1.9, 0.4, 0, 8, 10.3, '2024-11-07 06:25:00', 206, 'Breakfast', 7), +(231, 'Avocado', 537.6, 6.7, 49.1, 6.7, 28.6, 23.5, '2024-11-07 12:48:00', 336, 'Lunch', 7), +(232, 'Broccoli', 32, 2.6, 0.6, 0.2, 6.2, 62, '2024-11-07 12:48:00', 94, 'Lunch', 7), +(233, 'Brown rice', 477.4, 11.1, 6.6, 0.9, 99.5, 0, '2024-11-07 17:31:00', 221, 'Dinner', 7), +(234, 'Cucumber', 72.3, 3.2, 0.5, 0, 17.2, 9, '2024-11-08 07:09:00', 452, 'Breakfast', 7), +(235, 'Garlic', 512.4, 24.5, 56.4, 12.1, 22.7, 611.2, '2024-11-08 11:58:00', 366, 'Lunch', 7), +(236, 'Chicken thigh', 240.3, 32.9, 18.2, 6.3, 0, 0, '2024-11-08 11:58:00', 122, 'Lunch', 7), +(237, 'Onion', 137.2, 4.8, 0.3, 0, 31.9, 10.3, '2024-11-08 19:26:00', 343, 'Dinner', 7), +(238, 'Pork', 607.3, 38.1, 49.6, 18.4, 0, 123.5, '2024-11-08 19:26:00', 252, 'Dinner', 7), +(239, 'Banana', 297.2, 3.7, 0.8, 0.3, 76.4, 2.8, '2024-11-09 07:33:00', 283, 'Breakfast', 7), +(240, 'Chicken breast', 790.4, 148.5, 29.2, 9.6, 0, 330.5, '2024-11-09 07:33:00', 479, 'Breakfast', 7), +(241, 'Lamb', 311.6, 18.6, 24.6, 9.8, 0, 0, '2024-11-09 13:15:00', 106, 'Lunch', 7), +(242, 'Chicken thigh', 784.1, 107.5, 59.3, 20.7, 0, 0, '2024-11-09 13:15:00', 398, 'Lunch', 7), +(243, 'Apple', 116, 0, 0, 0, 31.2, 0, '2024-11-09 18:17:00', 223, 'Dinner', 7), +(244, 'Salmon', 945.5, 101, 62.4, 17.4, 22.9, 0, '2024-11-09 18:17:00', 459, 'Dinner', 7), +(245, 'Banana', 498.8, 6.2, 1.4, 0.5, 128.2, 4.8, '2024-11-10 06:40:00', 475, 'Breakfast', 7), +(246, 'Lamb', 1111.3, 66.1, 87.7, 34.8, 0, 0, '2024-11-10 11:17:00', 378, 'Lunch', 7), +(247, 'Avocado', 475.2, 5.9, 43.4, 5.9, 25.2, 20.8, '2024-11-10 11:17:00', 297, 'Lunch', 7), +(248, 'Banana', 183.8, 2.3, 0.5, 0.2, 47.2, 1.8, '2024-11-10 17:29:00', 175, 'Dinner', 7), +(249, 'Onion', 119.6, 4.2, 0.3, 0, 27.8, 9, '2024-11-04 07:58:00', 299, 'Breakfast', 8), +(250, 'Beef', 465, 40.9, 39.1, 14.9, 0, 93, '2024-11-04 13:10:00', 186, 'Lunch', 8), +(251, 'Bread', 871.9, 30.9, 15.1, 4.3, 171.1, 493.5, '2024-11-04 19:57:00', 329, 'Dinner', 8), +(252, 'Lamb', 884.9, 52.7, 69.8, 27.7, 0, 0, '2024-11-05 06:10:00', 301, 'Breakfast', 8), +(253, 'Milk', 230, 15.2, 15.2, 7.8, 22.1, 552, '2024-11-05 06:10:00', 460, 'Breakfast', 8), +(254, 'Lettuce', 35.8, 3.6, 0.9, 0.4, 7.6, 22.4, '2024-11-05 11:42:00', 448, 'Lunch', 8), +(255, 'Salmon', 786.9, 84, 52, 14.5, 19.1, 0, '2024-11-05 11:42:00', 382, 'Lunch', 8), +(256, 'Beef', 535, 47.1, 44.9, 17.1, 0, 107, '2024-11-05 19:16:00', 214, 'Dinner', 8), +(257, 'Cucumber', 71.2, 3.1, 0.4, 0, 16.9, 8.9, '2024-11-06 08:21:00', 445, 'Breakfast', 8), +(258, 'Chicken thigh', 396, 54.3, 29.9, 10.5, 0, 0, '2024-11-06 08:21:00', 201, 'Breakfast', 8), +(259, 'Chicken breast', 513.1, 96.4, 19, 6.2, 0, 214.6, '2024-11-06 12:27:00', 311, 'Lunch', 8), +(260, 'Sweet potato', 212.1, 3.4, 0.2, 0, 46.2, 6.3, '2024-11-06 18:02:00', 210, 'Dinner', 8), +(261, 'Chicken thigh', 439.3, 60.2, 33.2, 11.6, 0, 0, '2024-11-06 18:02:00', 223, 'Dinner', 8), +(262, 'Sweet potato', 211.1, 3.3, 0.2, 0, 46, 6.3, '2024-11-07 07:56:00', 209, 'Breakfast', 8), +(263, 'Pasta', 94.3, 4, 0.7, 0.1, 18.7, 0, '2024-11-07 07:56:00', 72, 'Breakfast', 8), +(264, 'Spinach', 74.5, 8.7, 1.3, 0.3, 11.7, 256, '2024-11-07 11:35:00', 324, 'Lunch', 8), +(265, 'Chicken breast', 669.9, 125.9, 24.8, 8.1, 0, 280.1, '2024-11-07 18:49:00', 406, 'Dinner', 8), +(266, 'Cheese', 888.4, 45.5, 73.2, 51.3, 2.9, 0, '2024-11-08 07:19:00', 221, 'Breakfast', 8), +(267, 'Potato', 168.6, 4.4, 0.2, 0, 37.9, 21.9, '2024-11-08 07:19:00', 219, 'Breakfast', 8), +(268, 'Banana', 203.7, 2.5, 0.6, 0.2, 52.4, 1.9, '2024-11-08 12:42:00', 194, 'Lunch', 8), +(269, 'Yogurt', 284.2, 10, 11.7, 6.5, 19.2, 240, '2024-11-08 12:42:00', 192, 'Lunch', 8), +(270, 'Sweet potato', 403, 6.4, 0.4, 0, 87.8, 12, '2024-11-08 18:37:00', 399, 'Dinner', 8), +(271, 'Chicken breast', 785.4, 147.6, 29, 9.5, 0, 328.4, '2024-11-09 07:08:00', 476, 'Breakfast', 8), +(272, 'Avocado', 612.8, 7.7, 55.9, 7.7, 32.6, 26.8, '2024-11-09 07:08:00', 383, 'Breakfast', 8), +(273, 'Egg', 131, 10.6, 9.4, 2.7, 0.7, 104.2, '2024-11-09 12:47:00', 168, 'Lunch', 8), +(274, 'Lettuce', 20.7, 2.1, 0.5, 0.3, 4.4, 12.9, '2024-11-09 12:47:00', 259, 'Lunch', 8), +(275, 'Salmon', 222.5, 23.8, 14.7, 4.1, 5.4, 0, '2024-11-09 18:20:00', 108, 'Dinner', 8), +(276, 'Spinach', 111.3, 13.1, 1.9, 0.5, 17.4, 382.4, '2024-11-10 06:56:00', 484, 'Breakfast', 8), +(277, 'Garlic', 460.6, 22, 50.7, 10.9, 20.4, 549.4, '2024-11-10 11:47:00', 329, 'Lunch', 8), +(278, 'Lamb', 643.9, 38.3, 50.8, 20.1, 0, 0, '2024-11-10 19:54:00', 219, 'Dinner', 8), +(279, 'Beef', 1125, 99, 94.5, 36, 0, 225, '2024-11-10 19:54:00', 450, 'Dinner', 8), +(280, 'Lamb', 929, 55.3, 73.3, 29.1, 0, 0, '2024-11-04 07:54:00', 316, 'Breakfast', 9), +(281, 'Pork', 443.4, 27.8, 36.2, 13.4, 0, 90.2, '2024-11-04 12:05:00', 184, 'Lunch', 9), +(282, 'Beef', 885, 77.9, 74.3, 28.3, 0, 177, '2024-11-04 12:05:00', 354, 'Lunch', 9), +(283, 'Brown rice', 276.5, 6.4, 3.8, 0.5, 57.6, 0, '2024-11-04 18:57:00', 128, 'Dinner', 9), +(284, 'Banana', 265.6, 3.3, 0.8, 0.3, 68.3, 2.5, '2024-11-04 18:57:00', 253, 'Dinner', 9), +(285, 'Bread', 1298.5, 46.1, 22.5, 6.4, 254.8, 735, '2024-11-05 07:40:00', 490, 'Breakfast', 9), +(286, 'Milk', 105, 6.9, 6.9, 3.6, 10.1, 252, '2024-11-05 07:40:00', 210, 'Breakfast', 9), +(287, 'Pasta', 531.9, 22.3, 4.1, 0.8, 105.6, 0, '2024-11-05 12:21:00', 406, 'Lunch', 9), +(288, 'Sweet potato', 169.7, 2.7, 0.2, 0, 37, 5, '2024-11-05 12:21:00', 168, 'Lunch', 9), +(289, 'Onion', 198.8, 7, 0.5, 0, 46.2, 14.9, '2024-11-05 19:11:00', 497, 'Dinner', 9), +(290, 'Avocado', 140.8, 1.8, 12.8, 1.8, 7.5, 6.2, '2024-11-05 19:11:00', 88, 'Dinner', 9), +(291, 'Lamb', 423.4, 25.2, 33.4, 13.2, 0, 0, '2024-11-06 07:39:00', 144, 'Breakfast', 9), +(292, 'Bread', 800.3, 28.4, 13.9, 3.9, 157, 453, '2024-11-06 13:54:00', 302, 'Lunch', 9), +(293, 'Carrot', 37.3, 0.8, 0.2, 0, 8.7, 62.8, '2024-11-06 13:54:00', 91, 'Lunch', 9), +(294, 'Broccoli', 49.3, 4.1, 0.9, 0.3, 9.6, 95.7, '2024-11-06 17:03:00', 145, 'Dinner', 9), +(295, 'Carrot', 123.4, 2.7, 0.6, 0, 28.9, 207.7, '2024-11-06 17:03:00', 301, 'Dinner', 9), +(296, 'Chicken thigh', 855, 117.2, 64.7, 22.6, 0, 0, '2024-11-07 08:12:00', 434, 'Breakfast', 9), +(297, 'Chicken breast', 585.8, 110, 21.7, 7.1, 0, 244.9, '2024-11-07 08:12:00', 355, 'Breakfast', 9), +(298, 'Salmon', 931.1, 99.4, 61.5, 17.2, 22.6, 0, '2024-11-07 12:45:00', 452, 'Lunch', 9), +(299, 'Bread', 556.5, 19.7, 9.7, 2.7, 109.2, 315, '2024-11-07 12:45:00', 210, 'Lunch', 9), +(300, 'Carrot', 96.8, 2.1, 0.5, 0, 22.7, 162.8, '2024-11-07 17:14:00', 236, 'Dinner', 9), +(301, 'Chicken thigh', 382.2, 52.4, 28.9, 10.1, 0, 0, '2024-11-08 06:20:00', 194, 'Breakfast', 9), +(302, 'Yogurt', 679.3, 23.9, 28, 15.6, 45.9, 573.8, '2024-11-08 06:20:00', 459, 'Breakfast', 9), +(303, 'Lettuce', 23.2, 2.3, 0.6, 0.3, 4.9, 14.5, '2024-11-08 13:27:00', 290, 'Lunch', 9), +(304, 'Lettuce', 28.4, 2.8, 0.7, 0.4, 6, 17.8, '2024-11-08 13:27:00', 355, 'Lunch', 9), +(305, 'Spinach', 77.7, 9.1, 1.4, 0.3, 12.2, 267, '2024-11-08 19:43:00', 338, 'Dinner', 9), +(306, 'Banana', 420, 5.2, 1.2, 0.4, 108, 4, '2024-11-09 06:39:00', 400, 'Breakfast', 9), +(307, 'Banana', 475.7, 5.9, 1.4, 0.5, 122.3, 4.5, '2024-11-09 12:24:00', 453, 'Lunch', 9), +(308, 'Beef', 1110, 97.7, 93.2, 35.5, 0, 222, '2024-11-09 17:23:00', 444, 'Dinner', 9), +(309, 'Lettuce', 36.2, 3.6, 0.9, 0.5, 7.7, 22.6, '2024-11-09 17:23:00', 452, 'Dinner', 9), +(310, 'Chicken thigh', 979.1, 134.2, 74.1, 25.8, 0, 0, '2024-11-10 07:24:00', 497, 'Breakfast', 9), +(311, 'Broccoli', 154.7, 12.7, 2.7, 0.9, 30, 300.3, '2024-11-10 07:24:00', 455, 'Breakfast', 9), +(312, 'Egg', 84.2, 6.8, 6, 1.7, 0.4, 67, '2024-11-10 12:42:00', 108, 'Lunch', 9), +(313, 'Pork', 903.8, 56.6, 73.9, 27.4, 0, 183.8, '2024-11-10 17:50:00', 375, 'Dinner', 9), +(314, 'Avocado', 348.8, 4.4, 31.8, 4.4, 18.5, 15.3, '2024-11-04 07:54:00', 218, 'Breakfast', 10), +(315, 'Spinach', 87.9, 10.3, 1.5, 0.4, 13.8, 301.8, '2024-11-04 07:54:00', 382, 'Breakfast', 10), +(316, 'Carrot', 203.4, 4.5, 1, 0, 47.6, 342.2, '2024-11-04 12:17:00', 496, 'Lunch', 10), +(317, 'Cucumber', 47.8, 2.1, 0.3, 0, 11.4, 6, '2024-11-04 12:17:00', 299, 'Lunch', 10), +(318, 'Yogurt', 217.6, 7.6, 9, 5, 14.7, 183.8, '2024-11-04 19:25:00', 147, 'Dinner', 10), +(319, 'Avocado', 281.6, 3.5, 25.7, 3.5, 15, 12.3, '2024-11-04 19:25:00', 176, 'Dinner', 10), +(320, 'Sweet potato', 168.7, 2.7, 0.2, 0, 36.7, 5, '2024-11-05 08:55:00', 167, 'Breakfast', 10), +(321, 'Milk', 187, 12.3, 12.3, 6.4, 18, 448.8, '2024-11-05 13:51:00', 374, 'Lunch', 10), +(322, 'Sweet potato', 95.9, 1.5, 0.1, 0, 20.9, 2.8, '2024-11-05 18:32:00', 95, 'Dinner', 10), +(323, 'Salmon', 883.7, 94.4, 58.3, 16.3, 21.4, 0, '2024-11-06 06:57:00', 429, 'Breakfast', 10), +(324, 'Yogurt', 125.8, 4.4, 5.2, 2.9, 8.5, 106.2, '2024-11-06 06:57:00', 85, 'Breakfast', 10), +(325, 'Cheese', 1949.7, 99.9, 160.5, 112.5, 6.3, 0, '2024-11-06 11:31:00', 485, 'Lunch', 10), +(326, 'Broccoli', 32, 2.6, 0.6, 0.2, 6.2, 62, '2024-11-06 11:31:00', 94, 'Lunch', 10), +(327, 'Sweet potato', 99, 1.6, 0.1, 0, 21.6, 2.9, '2024-11-06 18:01:00', 98, 'Dinner', 10), +(328, 'Broccoli', 133.3, 11, 2.4, 0.8, 25.9, 258.7, '2024-11-06 18:01:00', 392, 'Dinner', 10), +(329, 'Pasta', 615.7, 25.9, 4.7, 0.9, 122.2, 0, '2024-11-07 06:30:00', 470, 'Breakfast', 10), +(330, 'Banana', 113.4, 1.4, 0.3, 0.1, 29.2, 1.1, '2024-11-07 06:30:00', 108, 'Breakfast', 10), +(331, 'Bread', 429.3, 15.2, 7.5, 2.1, 84.2, 243, '2024-11-07 11:27:00', 162, 'Lunch', 10), +(332, 'Bread', 927.5, 32.9, 16.1, 4.5, 182, 525, '2024-11-07 11:27:00', 350, 'Lunch', 10), +(333, 'Brown rice', 995.8, 23.1, 13.8, 1.8, 207.5, 0, '2024-11-07 19:52:00', 461, 'Dinner', 10), +(334, 'Potato', 325.7, 8.5, 0.4, 0, 73.2, 42.3, '2024-11-08 06:05:00', 423, 'Breakfast', 10), +(335, 'Egg', 43.7, 3.5, 3.1, 0.9, 0.2, 34.7, '2024-11-08 12:47:00', 56, 'Lunch', 10), +(336, 'Potato', 139.4, 3.6, 0.2, 0, 31.3, 18.1, '2024-11-08 12:47:00', 181, 'Lunch', 10), +(337, 'Yogurt', 722.2, 25.4, 29.8, 16.6, 48.8, 610, '2024-11-08 19:02:00', 488, 'Dinner', 10), +(338, 'Egg', 373.6, 30.2, 26.8, 7.7, 1.9, 297, '2024-11-08 19:02:00', 479, 'Dinner', 10), +(339, 'Pasta', 313.1, 13.1, 2.4, 0.5, 62.1, 0, '2024-11-09 06:07:00', 239, 'Breakfast', 10), +(340, 'Egg', 213.7, 17.3, 15.3, 4.4, 1.1, 169.9, '2024-11-09 06:07:00', 274, 'Breakfast', 10), +(341, 'Garlic', 254.8, 12.2, 28, 6, 11.3, 303.9, '2024-11-09 11:31:00', 182, 'Lunch', 10), +(342, 'Sweet potato', 388.9, 6.2, 0.4, 0, 84.7, 11.6, '2024-11-09 11:31:00', 385, 'Lunch', 10), +(343, 'Banana', 158.6, 2, 0.5, 0.2, 40.8, 1.5, '2024-11-09 19:46:00', 151, 'Dinner', 10), +(344, 'Tomato', 15.7, 0.8, 0.2, 0, 3.4, 4.3, '2024-11-10 08:40:00', 87, 'Breakfast', 10), +(345, 'Carrot', 44.7, 1, 0.2, 0, 10.5, 75.2, '2024-11-10 12:45:00', 109, 'Lunch', 10), +(346, 'Sweet potato', 170.7, 2.7, 0.2, 0, 37.2, 5.1, '2024-11-10 19:16:00', 169, 'Dinner', 10), +(347, 'Egg', 624, 50.4, 44.8, 12.8, 3.2, 496, '2024-11-14 08:57:54', 800, 'Breakfast', 11), +(348, 'Spinach', 690, 81, 12, 3, 108, 2370, '2024-11-14 08:58:17', 3000, 'Dinner', 11), +(349, 'Chicken breast', 1650, 310, 61, 20, 10, 690, '2024-11-28 11:05:49', 1000, 'Breakfast', 1), +(350, 'Chicken breast', 1725.9, 324.26, 63.806, 20.92, 10.46, 721.74, '2024-11-28 15:04:46', 1046, 'Breakfast', 1); + +ALTER TABLE `food_items` + ADD PRIMARY KEY (`id`), + ADD KEY `user_id` (`user_id`); + + +ALTER TABLE `food_items` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=351; + +/* +ALTER TABLE `food_items` + ADD CONSTRAINT `food_items_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); + */ \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/08_alter_food_information.sql b/bin/main/db/changelog/changes/enson_change/08_alter_food_information.sql new file mode 100644 index 0000000..5103e00 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/08_alter_food_information.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`food_items` + add `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `createdBy` int DEFAULT NULL, + add `version` int NOT NULL DEFAULT '0', + add `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `modifiedBy` int DEFAULT NULL, + add `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/09_alter_food_information.sql b/bin/main/db/changelog/changes/enson_change/09_alter_food_information.sql new file mode 100644 index 0000000..4f616b8 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/09_alter_food_information.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +ALTER TABLE `food_items` +CHANGE COLUMN `kcal` `kcal` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `protein` `protein` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `fat` `fat` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `sat_fat` `sat_fat` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `carbo` `carbo` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `sodium` `sodium` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `size` `size` DECIMAL(14,2) NULL DEFAULT NULL ; diff --git a/bin/main/db/changelog/changes/enson_change/10_alter_food_items.sql b/bin/main/db/changelog/changes/enson_change/10_alter_food_items.sql new file mode 100644 index 0000000..b9bc01e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/10_alter_food_items.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql + +--changeset cyril:alter food items +--comment: alter food items + +ALTER TABLE `food_items` + CHANGE COLUMN `created` `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `id`, + CHANGE COLUMN `createdBy` `createdBy` INT NULL DEFAULT NULL AFTER `created`, + CHANGE COLUMN `version` `version` INT NOT NULL DEFAULT '0' AFTER `createdBy`, + CHANGE COLUMN `modified` `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `version`, + CHANGE COLUMN `modifiedBy` `modifiedBy` INT NULL DEFAULT NULL AFTER `modified`, + CHANGE COLUMN `deleted` `deleted` TINYINT(1) NOT NULL DEFAULT '0' AFTER `modifiedBy`; diff --git a/bin/main/db/changelog/changes/enson_change/11_create_user_information.sql b/bin/main/db/changelog/changes/enson_change/11_create_user_information.sql new file mode 100644 index 0000000..7dfe625 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/11_create_user_information.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `user_information` ( + `id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `gender` varchar(10) DEFAULT NULL, + `name` varchar(50) DEFAULT NULL, + `date_of_birth` date DEFAULT NULL, + `height` varchar(20) DEFAULT NULL, + `weight` varchar(20) DEFAULT NULL, + `allergy` varchar(50) DEFAULT NULL, + `goal` varchar(20) DEFAULT NULL, + `experience` varchar(20) DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/12_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/12_alter_user_information.sql new file mode 100644 index 0000000..cb8cba3 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/12_alter_user_information.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Alter table and insert data + +INSERT INTO `user_information` (`id`, `user_id`, `gender`, `name`, `date_of_birth`, `height`, `weight`, `allergy`, `goal`, `experience`, `created_at`) VALUES +(2, 1, 'Male', 'test', '2000-01-01', '175 cm', '61 kg', 'Peanuts', 'Muscle Gain', 'Expert', '2024-11-13 09:23:46'), +(3, 3, 'Male', 'm2', '2003-01-01', '162 cm', '80 kg', 'Egg', 'Muscle Gain', 'Intermediate', '2024-11-13 17:18:38'), +(4, 2, 'Male', 'm1', '2000-01-01', '162 cm', '53 kg', 'Fish', 'Muscle Gain', 'Expert', '2024-11-13 17:25:06'), +(5, 4, 'Male', 'm3', '2018-01-01', '164 cm', '125 lbs', 'Fish, Peanuts, Wheat', 'Muscle Gain', 'Intermediate', '2024-11-13 17:28:51'), +(6, 5, 'Male', 'm4', '2008-12-15', '169 cm', '108 kg', 'Peanuts, Fish', 'Nutrition', 'Beginner', '2024-11-13 17:32:53'), +(7, 6, 'Female', 'g1', '2015-01-01', '158 cm', '46 kg', 'Egg, Fish, Peanuts, Wheat, Soy, Milk', 'Muscle Gain', 'Expert', '2024-11-13 17:35:00'), +(8, 10, 'Female', 'g5', '1992-05-01', '213 cm', '140 kg', 'None', 'Muscle Gain', 'Beginner', '2024-11-13 17:37:00'), +(9, 9, 'Female', 'g4', '1989-01-01', '4 ft 11 in', '54 kg', 'Wheat', 'Nutrition', 'Beginner', '2024-11-13 17:37:25'), +(10, 8, 'Female', 'g3', '2006-06-03', '6 ft 4 in', '89 lbs', 'Egg', 'Diet', 'Intermediate', '2024-11-13 17:38:21'), +(11, 7, 'Female', 'g2', '2000-01-01', '170 cm', '60 kg', 'None', 'Diet', 'Beginner', '2024-11-13 17:38:46'), +(12, 11, 'Male', 'example', '2022-07-01', '171 cm', '54 kg', 'Peanuts, Wheat', 'Diet', 'Intermediate', '2024-11-14 08:58:59'); + +ALTER TABLE `user_information` + ADD PRIMARY KEY (`id`), + ADD KEY `user_id` (`user_id`); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/13_alter_information.sql b/bin/main/db/changelog/changes/enson_change/13_alter_information.sql new file mode 100644 index 0000000..9d1a2ad --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/13_alter_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user-table +--comment: Alter table + +ALTER TABLE `fhsmscdb`.`user` + ADD `has_information` TINYINT NOT NULL DEFAULT 0; diff --git a/bin/main/db/changelog/changes/enson_change/14_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/14_alter_user_information.sql new file mode 100644 index 0000000..22f7bd6 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/14_alter_user_information.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + add `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `createdBy` int DEFAULT NULL, + add `version` int NOT NULL DEFAULT '0', + add `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `modifiedBy` int DEFAULT NULL, + add `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/15_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/15_alter_user_information.sql new file mode 100644 index 0000000..decf572 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/15_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + DROP COLUMN `created_at`; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/16_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/16_alter_user_information.sql new file mode 100644 index 0000000..2cf9807 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/16_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=27; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/17_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/17_alter_user_information.sql new file mode 100644 index 0000000..4dbcb5f --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/17_alter_user_information.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `age` int DEFAULT NULL, + add `activityLevel` varchar(20) DEFAULT NULL; diff --git a/bin/main/db/changelog/changes/enson_change/18_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/18_alter_user_information.sql new file mode 100644 index 0000000..de7a3d4 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/18_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `activityLevel` varchar(20) DEFAULT NULL; diff --git a/bin/main/db/changelog/changes/enson_change/19_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/19_alter_user_information.sql new file mode 100644 index 0000000..100883a --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/19_alter_user_information.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY COLUMN `age` int DEFAULT NULL AFTER `gender`, + MODIFY COLUMN `activityLevel` varchar(20) DEFAULT NULL AFTER `experience`; diff --git a/bin/main/db/changelog/changes/enson_change/20_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/20_alter_food1.sql new file mode 100644 index 0000000..540ca11 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/20_alter_food1.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Weight (g)` decimal(10,2) DEFAULT NULL, +ADD COLUMN `contains_egg` tinyint DEFAULT 3, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `contains_milk` tinyint DEFAULT 3, +ADD COLUMN `contains_wheat` tinyint DEFAULT 3, +ADD COLUMN `contains_shrimp` tinyint DEFAULT 3, +ADD COLUMN `contains_crab` tinyint DEFAULT 3, +ADD COLUMN `contains_buckwheat` tinyint DEFAULT 3, +ADD COLUMN `contains_peanut` tinyint DEFAULT 3, +ADD COLUMN `contains_walnut` tinyint DEFAULT 3; + +-- 重命名现有列以添加单位 +ALTER TABLE `fhsmscdb`.`food1` +CHANGE COLUMN `kcal` `Calories (kcal)` decimal(10,2), +CHANGE COLUMN `protein` `Protein (g)` decimal(10,2), +CHANGE COLUMN `fat` `Fat (g)` decimal(10,2), +DROP COLUMN `satFat`, +CHANGE COLUMN `carbo` `Carbohydrate (g)` decimal(10,2), +CHANGE COLUMN `sodium` `Sodium (mg)` decimal(10,2); + diff --git a/bin/main/db/changelog/changes/enson_change/21_alter_food_items.sql b/bin/main/db/changelog/changes/enson_change/21_alter_food_items.sql new file mode 100644 index 0000000..5c9388f --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/21_alter_food_items.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + + + +-- 重命名现有列以添加单位 +ALTER TABLE `fhsmscdb`.`food_items` +DROP COLUMN `sat_fat`; + + diff --git a/bin/main/db/changelog/changes/enson_change/22_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/22_alter_food1.sql new file mode 100644 index 0000000..3352387 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/22_alter_food1.sql @@ -0,0 +1,16 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Postassium (mg)` decimal(10,2) DEFAULT 0, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `Calcium (mg)`decimal(10,2) DEFAULT 0, +ADD COLUMN `Phosphorus (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Iron (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin A (μg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin B1 (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin B2 (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Niacin (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin C (mg)` decimal(10,2) DEFAULT 0; + diff --git a/bin/main/db/changelog/changes/enson_change/23_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/23_alter_food1.sql new file mode 100644 index 0000000..94c15e5 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/23_alter_food1.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +DROP COLUMN `Ingredients`; +ALTER TABLE `fhsmscdb`.`food1` +MODIFY COLUMN `Postassium (mg)` decimal(10,2) DEFAULT 0 AFTER `Sodium (mg)`, +MODIFY COLUMN `Calcium (mg)` decimal(10,2) DEFAULT 0 AFTER `Postassium (mg)`, +MODIFY COLUMN `Phosphorus (mg)` decimal(10,2) DEFAULT 0 AFTER `Calcium (mg)`, +MODIFY COLUMN `Iron (mg)` decimal(10,2) DEFAULT 0 AFTER `Phosphorus (mg)`, +MODIFY COLUMN `Vitamin A (μg)` decimal(10,2) DEFAULT 0 AFTER `Iron (mg)`, +MODIFY COLUMN `Vitamin B1 (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin A (μg)`, +MODIFY COLUMN `Vitamin B2 (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin B1 (mg)`, +MODIFY COLUMN `Niacin (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin B2 (mg)`, +MODIFY COLUMN `Vitamin C (mg)` decimal(10,2)DEFAULT 0 AFTER `Niacin (mg)`; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/24_alter_food_items.sql b/bin/main/db/changelog/changes/enson_change/24_alter_food_items.sql new file mode 100644 index 0000000..6dad3d5 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/24_alter_food_items.sql @@ -0,0 +1,27 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +-- 添加新列 +/* +Alter TABLE `fhsmscdb`.`food_items` +ADD COLUMN `potassium` decimal(10,2) DEFAULT 0, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `calcium`decimal(10,2) DEFAULT 0, +ADD COLUMN `phosphorus` decimal(10,2) DEFAULT 0, +ADD COLUMN `iron` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminA` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminB1` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminB2` decimal(10,2) DEFAULT 0, +ADD COLUMN `niacin` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminC` decimal(10,2) DEFAULT 0; +*/ +ALTER TABLE `fhsmscdb`.`food_items` +MODIFY COLUMN `potassium` decimal(10,2) DEFAULT 0 AFTER `sodium`, +MODIFY COLUMN `calcium` decimal(10,2) DEFAULT 0 AFTER `potassium`, +MODIFY COLUMN `phosphorus` decimal(10,2) DEFAULT 0 AFTER `calcium`, +MODIFY COLUMN `iron` decimal(10,2) DEFAULT 0 AFTER `phosphorus`, +MODIFY COLUMN `vitaminA` decimal(10,2) DEFAULT 0 AFTER `iron`, +MODIFY COLUMN `vitaminB1` decimal(10,2) DEFAULT 0 AFTER `vitaminA`, +MODIFY COLUMN `vitaminB2` decimal(10,2) DEFAULT 0 AFTER `vitaminB1`, +MODIFY COLUMN `niacin` decimal(10,2) DEFAULT 0 AFTER `vitaminB2`, +MODIFY COLUMN `vitaminC` decimal(10,2)DEFAULT 0 AFTER `niacin`; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/25_create_Nutrients.sql b/bin/main/db/changelog/changes/enson_change/25_create_Nutrients.sql new file mode 100644 index 0000000..5a8f65e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/25_create_Nutrients.sql @@ -0,0 +1,40 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Nutrients` ( + `nutrient_id` int NOT NULL AUTO_INCREMENT, + `nutrient_name` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`nutrient_id`), + UNIQUE KEY `idx_nutrient_name` (`nutrient_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `Nutrient_Impacts` ( + `impact_id` int NOT NULL AUTO_INCREMENT, + `nutrient_id` int NOT NULL, + `organ_affected` varchar(100) NOT NULL, + `impact_type` ENUM('Positive', 'Negative') NOT NULL, + `impact_description` text NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`impact_id`), + FOREIGN KEY (`nutrient_id`) REFERENCES `Nutrients`(`nutrient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入基础营养素数据 +INSERT INTO `Nutrients` (nutrient_name) VALUES +('Calories'), +('Protein'), +('Fat'), +('Carbohydrate'), +('Sodium'), +('Potassium'), +('Calcium'), +('Phosphorus'), +('Iron'), +('Vitamin A'), +('Vitamin B1'), +('Vitamin B2'), +('Niacin'), +('Vitamin C'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql b/bin/main/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql new file mode 100644 index 0000000..1d3d939 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql @@ -0,0 +1,26 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +INSERT INTO `Nutrient_Impacts` +(nutrient_id, organ_affected, impact_type, impact_description) +VALUES +(2, 'Muscles', 'Positive', 'Essential for muscle growth and repair'), +(2, 'Bones', 'Positive', 'Helps maintain bone mass'), +(2, 'Immune System', 'Positive', 'Supports immune system function'), + +-- Vitamin C impacts (假设 nutrient_id = 14) +(14, 'Immune System', 'Positive', 'Strengthens immune system and fights infections'), +(14, 'Skin', 'Positive', 'Promotes collagen production for healthy skin'), +(14, 'Blood Vessels', 'Positive', 'Supports blood vessel health'), + +-- Iron impacts (假设 nutrient_id = 9) +(9, 'Blood', 'Positive', 'Essential for red blood cell production'), +(9, 'Brain', 'Positive', 'Supports cognitive function'), +(9, 'Immune System', 'Positive', 'Helps maintain immune system'), + +-- Calcium impacts (假设 nutrient_id = 7) +(7, 'Bones', 'Positive', 'Essential for bone strength and density'), +(7, 'Teeth', 'Positive', 'Maintains dental health'), +(7, 'Muscles', 'Positive', 'Required for proper muscle function'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/27_create_Allergy.sql b/bin/main/db/changelog/changes/enson_change/27_create_Allergy.sql new file mode 100644 index 0000000..9158c2f --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/27_create_Allergy.sql @@ -0,0 +1,49 @@ +/* +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Allergies` ( + `allergy_id` int NOT NULL AUTO_INCREMENT, + `allergy_name` varchar(50) NOT NULL, + `description` text, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`allergy_id`), + UNIQUE KEY `idx_allergy_name` (`allergy_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 用户过敏记录表 +CREATE TABLE `User_Allergies` ( + `user_allergy_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `allergy_id` int NOT NULL, + `diagnosed_date` date, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_allergy_id`), + UNIQUE KEY `idx_user_allergy` (`user_id`, `allergy_id`), + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), + FOREIGN KEY (`allergy_id`) REFERENCES `Allergies`(`allergy_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入常见过敏原数据 +INSERT INTO `Allergies` (allergy_name, description) VALUES +('Peanuts', 'Common legume that can cause severe allergic reactions'), +('Tree Nuts', 'Including almonds, walnuts, cashews, etc.'), +('Milk', 'Dairy products and lactose'), +('Eggs', 'Both egg whites and yolks'), +('Soy', 'Soybeans and soy-based products'), +('Wheat', 'Including gluten sensitivity'), +('Fish', 'Various types of fish'), +('Shellfish', 'Crustaceans and mollusks'), +('Sesame', 'Seeds and sesame oil'), +('Sulfites', 'Common food preservative'); + +-- 插入示例用户过敏数据 +INSERT INTO `User_Allergies` +(user_id, allergy_id, severity_level, symptoms, diagnosed_date) +VALUES +(1, 1, 'Severe', 'Anaphylaxis, difficulty breathing, swelling', '2023-01-15'), +(1, 3, 'Moderate', 'Digestive issues, skin rash', '2023-02-20'), +(2, 4, 'Mild', 'Mild skin irritation', '2023-03-10'), +(2, 8, 'Severe', 'Severe rash, breathing difficulties', '2023-04-05'); +*/ \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/28_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/28_alter_food1.sql new file mode 100644 index 0000000..54c725a --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/28_alter_food1.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Restaurant_ID` int(11) DEFAULT NULL; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/29_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/29_alter_food1.sql new file mode 100644 index 0000000..4f03ee5 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/29_alter_food1.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +INSERT INTO `food1` (`name`, `Calories (kcal)`, `Protein (g)`, `Fat (g)`, `Carbohydrate (g)`, `Sodium (mg)`, `Postassium (mg)`, `Calcium (mg)`, `Phosphorus (mg)`,`Iron (mg)`,`Vitamin A (μg)`,`Vitamin B1 (mg)`,`Vitamin B2 (mg)`,`Niacin (mg)`,`Vitamin C (mg)`,`food_type`,`Weight (g)`,`contains_egg`, `contains_milk`, `contains_wheat`, `contains_shrimp`,`contains_crab`,`contains_buckwheat`,`contains_peanut`,`contains_walnut`,`Restaurant_ID`) VALUES +('N.Y. Barbecue Sausage & Cheese McMuffin',504,16.6,32.9,35.1,952,263,162,190,1.1,60,0.2,0.21,6,3,'food',152,0,0,0,3,3,3,3,3,1); diff --git a/bin/main/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql b/bin/main/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql new file mode 100644 index 0000000..6af4d2e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Nutrients_Need` ( + `id` int NOT NULL, + `bmr` DECIMAL(10,2) NOT NULL, + `tdee` DECIMAL(10,2) NOT NULL, + `protein_need` DECIMAL(10,2) NOT NULL, + `fat_need` DECIMAL(10,2) NOT NULL, + `carbo_need` DECIMAL(10,2) NOT NULL, + `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/bin/main/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql b/bin/main/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql new file mode 100644 index 0000000..2c04e59 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql @@ -0,0 +1,64 @@ +--liquibase formatted sql +--changeset terence:create-update-trigger splitStatements:false +--comment: Create update trigger + +CREATE TRIGGER update_user_Nutrients_Need +AFTER UPDATE ON user_information +FOR EACH ROW +BEGIN + DECLARE calculated_bmr DECIMAL(10,2); + DECLARE calculated_tdee DECIMAL(10,2); + DECLARE user_age INT; + DECLARE weight_kg DECIMAL(5,2); + DECLARE height_cm DECIMAL(5,2); + + -- Extract numeric values from weight and height + SET weight_kg = CAST(REPLACE(NEW.weight, ' kg', '') AS DECIMAL(5,2)); + SET height_cm = CAST(REPLACE(NEW.height, ' cm', '') AS DECIMAL(5,2)); + + -- Calculate age + SET user_age = FLOOR(DATEDIFF(CURRENT_DATE, FROM_UNIXTIME(NEW.date_of_birth/1000)) / 365); + + -- Calculate BMR based on gender + IF NEW.gender = 'Male' THEN + SET calculated_bmr = 88.362 + (13.397 * weight_kg) + (4.799 * height_cm) - (5.677 * user_age); + ELSE + SET calculated_bmr = 447.593 + (9.247 * weight_kg) + (3.098 * height_cm) - (4.330 * user_age); + END IF; + + -- Calculate TDEE based on activity level + SET calculated_tdee = calculated_bmr * + CASE NEW.activitylevel + WHEN 'Sedentary' THEN 1.2 + WHEN '1-2 times' THEN 1.375 + WHEN '3-5 times' THEN 1.55 + WHEN '6-7 times' THEN 1.725 + WHEN 'Professional' THEN 1.9 + ELSE 1.2 + END; + + -- Insert or update TDEE record + INSERT INTO User_TDEE ( + user_id, + bmr, + tdee, + protein_need, + fat_need, + carbo_need + ) + VALUES ( + NEW.user_id, + calculated_bmr, + calculated_tdee, + weight_kg * 2, + (calculated_tdee * 0.25) / 9, + (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4 + ) + ON DUPLICATE KEY UPDATE + bmr = calculated_bmr, + tdee = calculated_tdee, + protein_need = weight_kg * 2, + fat_need = (calculated_tdee * 0.25) / 9, + carbo_need = (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4, + last_updated = CURRENT_TIMESTAMP; +END; diff --git a/bin/main/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql b/bin/main/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql new file mode 100644 index 0000000..946d78a --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql @@ -0,0 +1,64 @@ +--liquibase formatted sql +--changeset terence:create-update-trigger splitStatements:false +--comment: Create update trigger + +CREATE TRIGGER update_user_Nutrients_Need +AFTER UPDATE ON user_information +FOR EACH ROW +BEGIN + DECLARE calculated_bmr DECIMAL(10,2); + DECLARE calculated_tdee DECIMAL(10,2); + DECLARE user_age INT; + DECLARE weight_kg DECIMAL(5,2); + DECLARE height_cm DECIMAL(5,2); + + -- Extract numeric values from weight and height + SET weight_kg = CAST(REPLACE(NEW.weight, ' kg', '') AS DECIMAL(5,2)); + SET height_cm = CAST(REPLACE(NEW.height, ' cm', '') AS DECIMAL(5,2)); + + -- Calculate age + SET user_age = FLOOR(DATEDIFF(CURRENT_DATE, FROM_UNIXTIME(NEW.date_of_birth/1000)) / 365); + + -- Calculate BMR based on gender + IF NEW.gender = 'Male' THEN + SET calculated_bmr = 88.362 + (13.397 * weight_kg) + (4.799 * height_cm) - (5.677 * user_age); + ELSE + SET calculated_bmr = 447.593 + (9.247 * weight_kg) + (3.098 * height_cm) - (4.330 * user_age); + END IF; + + -- Calculate TDEE based on activity level + SET calculated_tdee = calculated_bmr * + CASE NEW.activitylevel + WHEN 'Sedentary' THEN 1.2 + WHEN '1-2 times' THEN 1.375 + WHEN '3-5 times' THEN 1.55 + WHEN '6-7 times' THEN 1.725 + WHEN 'Professional' THEN 1.9 + ELSE 1.2 + END; + + -- Insert or update TDEE record + INSERT INTO nutrients_need ( + user_id, + bmr, + tdee, + protein_need, + fat_need, + carbo_need + ) + VALUES ( + NEW.user_id, + calculated_bmr, + calculated_tdee, + weight_kg * 2, + (calculated_tdee * 0.25) / 9, + (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4 + ) + ON DUPLICATE KEY UPDATE + bmr = calculated_bmr, + tdee = calculated_tdee, + protein_need = weight_kg * 2, + fat_need = (calculated_tdee * 0.25) / 9, + carbo_need = (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4, + last_updated = CURRENT_TIMESTAMP; +END; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/33_create_Allergy.sql b/bin/main/db/changelog/changes/enson_change/33_create_Allergy.sql new file mode 100644 index 0000000..cf83fa7 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/33_create_Allergy.sql @@ -0,0 +1,44 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +CREATE TABLE `Allergies` ( + `allergy_id` int NOT NULL AUTO_INCREMENT, + `allergy_name` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`allergy_id`), + UNIQUE KEY `idx_allergy_name` (`allergy_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +*/ +CREATE TABLE `User_Allergies` ( + `user_allergy_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `allergy_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_allergy_id`), + UNIQUE KEY `idx_user_allergy` (`user_id`, `allergy_id`), + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), + FOREIGN KEY (`allergy_id`) REFERENCES `Allergies`(`allergy_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入常见过敏原数据 +INSERT INTO `Allergies` (allergy_name) VALUES +('Peanuts'), +('Tree Nuts'), +('Milk'), +('Eggs'), +('Soy' ), +('Wheat'), +('Fish'), +('Shellfish'), +('Sesame'), +('Sulfites'); + +-- 插入示例用户过敏数据 +INSERT INTO `User_Allergies` +(user_id, allergy_id ) +VALUES +(1, 1), +(1, 3), +(2, 4), +(2, 8); diff --git a/bin/main/db/changelog/changes/enson_change/34_alter_food1.sql b/bin/main/db/changelog/changes/enson_change/34_alter_food1.sql new file mode 100644 index 0000000..1af82b9 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/34_alter_food1.sql @@ -0,0 +1,19 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +Alter TABLE `fhsmscdb`.`food1` + CHANGE `Calories (kcal)` Calories decimal(10,2), + CHANGE `Protein (g)` Protein decimal(10,2), + CHANGE `Fat (g)` Fat decimal(10,2), + CHANGE `Carbohydrate (g)` Carbohydrate decimal(10,2), + CHANGE `Sodium (mg)` Sodium decimal(10,2), + CHANGE `Postassium (mg)` Potassium decimal(10,2), + CHANGE `Calcium (mg)` Calcium decimal(10,2), + CHANGE `Phosphorus (mg)` Phosphorus decimal(10,2), + CHANGE `Iron (mg)` Iron decimal(10,2), + CHANGE `Vitamin A (μg)` VitaminA decimal(10,2), + CHANGE `Vitamin B1 (mg)` VitaminB1 decimal(10,2), + CHANGE `Vitamin B2 (mg)` VitaminB2 decimal(10,2), + CHANGE `Niacin (mg)` Niacin decimal(10,2), + CHANGE `Vitamin C (mg)` VitaminC decimal(10,2), + CHANGE `Weight (g)` Weight decimal(10,2); diff --git a/bin/main/db/changelog/changes/enson_change/35_alter_user_information.sql b/bin/main/db/changelog/changes/enson_change/35_alter_user_information.sql new file mode 100644 index 0000000..b0069cf --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/35_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +Alter TABLE `fhsmscdb`.`user_information` + drop column `allergy`; + diff --git a/bin/main/db/changelog/changes/enson_change/36_create_restaurant_information.sql b/bin/main/db/changelog/changes/enson_change/36_create_restaurant_information.sql new file mode 100644 index 0000000..5a23e72 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/36_create_restaurant_information.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `FB_Operator` ( + + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_name` varchar(50) NOT NULL, + `FB_type` varchar(255) NOT NULL, + `FB_cuisine` varchar(20) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`) +)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/bin/main/db/changelog/changes/enson_change/37_alter_restaurant_information.sql b/bin/main/db/changelog/changes/enson_change/37_alter_restaurant_information.sql new file mode 100644 index 0000000..a46cb1b --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/37_alter_restaurant_information.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +INSERT INTO `FB_Operator` +(FB_id, FB_name, FB_type, FB_cuisine) +VALUES +(1, 'KFC', 'Fast Food', 'Fast Food'), +(2, 'McDonald', 'Fast Food', 'Fast Food'), +(3, '大家樂', 'Cha Chaan Teng', 'Hong Kong Style'), +(4, '翠華餐廳', 'Cha Chaan Teng', 'Hong Kong Style'), +(5, '美心', 'Chinese Restaurant', 'Cantonese'), +(6, '點點心', 'Dim Sum', 'Cantonese'), +(7, '吉野家', 'Fast Food', 'Japanese'), +(8, '壽司郎', 'Japanese Restaurant', 'Japanese'), +(9, '譚仔雲南米線', 'Noodle Shop', 'Chinese'), +(10, '大快活', 'Fast Food', 'Hong Kong Style') diff --git a/bin/main/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql new file mode 100644 index 0000000..c1f612d --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql @@ -0,0 +1,14 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `FB_Outlets` ( + + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_outlet_id` int NOT NULL, + `FB_outlet_district` varchar(255) NOT NULL, + `FB_outlet_address` varchar(255) NOT NULL, + `FB_outlet_phone` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`) +)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/bin/main/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql new file mode 100644 index 0000000..40b5021 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql @@ -0,0 +1,14 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_id` int NOT NULL; +ALTER TABLE `FB_Outlets` DROP PRIMARY KEY; + +ALTER TABLE `FB_Outlets` ADD PRIMARY KEY (`FB_outlet_id`); + + +ALTER TABLE `FB_Outlets` ADD CONSTRAINT `fk_fb_outlets_operator` +FOREIGN KEY (`FB_id`) REFERENCES `fb_operator`(`FB_id`); + + +ALTER TABLE `FB_Outlets` ADD INDEX `idx_fb_id` (`FB_id`); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql new file mode 100644 index 0000000..966b425 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_outlet_id` int NOT NULL FIRST; + + +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_id` int NOT NULL AFTER `FB_outlet_id`; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql new file mode 100644 index 0000000..2d17218 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +DROP TABLE `FB_Outlets`; + +-- 用新结构创建表 +CREATE TABLE `FB_Outlets` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_outlet_id` int NOT NULL, + `FB_id` int NOT NULL, + `FB_outlet_district` varchar(255), + `FB_outlet_address` varchar(255), + `FB_outlet_phone` varchar(255), + `created_at` timestamp, + PRIMARY KEY (`id`), + INDEX `idx_fb_outlet_id` (`FB_outlet_id`), + INDEX `idx_fb_id` (`FB_id`), + CONSTRAINT `fk_fb_outlets_operator` FOREIGN KEY (`FB_id`) + REFERENCES `fb_operator` (`FB_id`) +); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql new file mode 100644 index 0000000..364fe21 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +INSERT INTO `FB_Outlets` (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(201, 2, '金鐘', '夏愨道18號海富中心地下1-41號舖A部份', '2520 1329', CURRENT_TIMESTAMP), +(202, 2, '灣仔', '軒尼詩道302-308號集成中心Shop G11-17 ,G/F', '2893 9503', CURRENT_TIMESTAMP); + + +INSERT INTO FB_Outlets (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(301, 3, '銅鑼灣', '波斯富街48號軒尼詩大廈3樓', '2808 0139', CURRENT_TIMESTAMP), +(302, 3, '銅鑼灣', '地下英皇道6-8 號香港天后百利大廈', '2807 3864', CURRENT_TIMESTAMP); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql b/bin/main/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql new file mode 100644 index 0000000..76c796d --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql @@ -0,0 +1,8 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +INSERT INTO `fb_outlets` (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(201, 2, '金鐘', '夏愨道18號海富中心地下1-41號舖A部份', '2520 1329', CURRENT_TIMESTAMP), +(202, 2, '灣仔', '軒尼詩道302-308號集成中心Shop G11-17 ,G/F', '2893 9503', CURRENT_TIMESTAMP); + diff --git a/bin/main/db/changelog/changes/enson_change/44_create_muti_table.sql b/bin/main/db/changelog/changes/enson_change/44_create_muti_table.sql new file mode 100644 index 0000000..93df43d --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/44_create_muti_table.sql @@ -0,0 +1,71 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- 餐厅菜单表 +CREATE TABLE `FB_Menu` ( + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Menu_Item_Description` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`), + UNIQUE KEY `UK_FB_menu_id` (`FB_menu_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 菜单项成分表 +CREATE TABLE `FB_Menu_Item_Ingredient` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Ingredient_id` int NOT NULL, + `Ingredient_Volume` decimal(10,2) NOT NULL, + `Ingredient_Volume_Unit` varchar(50) NOT NULL, + `Allergy_Substance_ID` int, + `Nutrient_ID` int, + `Nutrient_Volume` decimal(10,2), + `Nutrient_Volume_Unit` varchar(50), + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 菜单与门店关系表 +CREATE TABLE `FB_Menu_and_Outlet_Relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `FB_outlet_id` int NOT NULL, + `FB_menu_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_rel_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_rel_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 每日营养摄入汇总表 +CREATE TABLE `daily_nutrition_summary` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `date` date NOT NULL, + `total_calories` decimal(10,2) DEFAULT 0, + `total_protein` decimal(10,2) DEFAULT 0, + `total_fat` decimal(10,2) DEFAULT 0, + `total_carbs` decimal(10,2) DEFAULT 0, + `total_sodium` decimal(10,2) DEFAULT 0, + `total_potassium` decimal(10,2) DEFAULT 0, + `total_calcium` decimal(10,2) DEFAULT 0, + `total_phosphorus` decimal(10,2) DEFAULT 0, + `total_iron` decimal(10,2) DEFAULT 0, + `total_vitamin_a` decimal(10,2) DEFAULT 0, + `total_vitamin_b1` decimal(10,2) DEFAULT 0, + `total_vitamin_b2` decimal(10,2) DEFAULT 0, + `total_niacin` decimal(10,2) DEFAULT 0, + `total_vitamin_c` decimal(10,2) DEFAULT 0, + `breakfast_calories` decimal(10,2) DEFAULT 0, + `lunch_calories` decimal(10,2) DEFAULT 0, + `dinner_calories` decimal(10,2) DEFAULT 0, + `snack_calories` decimal(10,2) DEFAULT 0, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_user_date` (`user_id`, `date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/45_alter_muti_table.sql b/bin/main/db/changelog/changes/enson_change/45_alter_muti_table.sql new file mode 100644 index 0000000..7cb0ec1 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/45_alter_muti_table.sql @@ -0,0 +1,43 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +CREATE TABLE `FB_Menu_Item_Nutrition` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Calories` decimal(10,2) DEFAULT 0, + `Protein` decimal(10,2) DEFAULT 0, + `Fat` decimal(10,2) DEFAULT 0, + `Carbohydrate` decimal(10,2) DEFAULT 0, + `Sodium` decimal(10,2) DEFAULT 0, + `Potassium` decimal(10,2) DEFAULT 0, + `Calcium` decimal(10,2) DEFAULT 0, + `Phosphorus` decimal(10,2) DEFAULT 0, + `Iron` decimal(10,2) DEFAULT 0, + `VitaminA` decimal(10,2) DEFAULT 0, + `VitaminB1` decimal(10,2) DEFAULT 0, + `VitaminB2` decimal(10,2) DEFAULT 0, + `Niacin` decimal(10,2) DEFAULT 0, + `VitaminC` decimal(10,2) DEFAULT 0, + `Nutrient_Volume` decimal(10,2) DEFAULT 0, + `Nutrient_Volume_Unit` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `UK_FB_menu_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_nutrition_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +*/ +CREATE TABLE `Ingredient` ( + `Ingredient_id` int NOT NULL AUTO_INCREMENT, + `Ingredient_Name` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`Ingredient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +ALTER TABLE `fhsmscdb`.`fb_menu_item_ingredient` + DROP COLUMN `Nutrient_ID`, + DROP COLUMN `Nutrient_Volume`, + DROP COLUMN `Nutrient_Volume_Unit`; + + + diff --git a/bin/main/db/changelog/changes/enson_change/46_alter_muti_table.sql b/bin/main/db/changelog/changes/enson_change/46_alter_muti_table.sql new file mode 100644 index 0000000..44e0013 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/46_alter_muti_table.sql @@ -0,0 +1,175 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +DROP TABLE IF EXISTS fb_menu_and_outlet_relationship; +DROP TABLE IF EXISTS fb_menu_item_ingredient; +DROP TABLE IF EXISTS fb_menu_item_nutrition; +DROP TABLE IF EXISTS fb_menu; +DROP TABLE IF EXISTS ingredient; + + +CREATE TABLE `ingredient` ( + `Ingredient_id` int NOT NULL AUTO_INCREMENT, + `Ingredient_Name` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`Ingredient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 重新创建fb_menu表,使用FB_menu_id作为主键 +CREATE TABLE `fb_menu` ( + `FB_id` int NOT NULL, + `FB_menu_id` int NOT NULL AUTO_INCREMENT, + `Menu_Item_Description` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_menu_id`), + INDEX `idx_FB_id` (`FB_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `fb_menu_item_nutrition` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Calories` decimal(10,2) DEFAULT 0, + `Protein` decimal(10,2) DEFAULT 0, + `Fat` decimal(10,2) DEFAULT 0, + `Carbohydrate` decimal(10,2) DEFAULT 0, + `Sodium` decimal(10,2) DEFAULT 0, + `Potassium` decimal(10,2) DEFAULT 0, + `Calcium` decimal(10,2) DEFAULT 0, + `Phosphorus` decimal(10,2) DEFAULT 0, + `Iron` decimal(10,2) DEFAULT 0, + `VitaminA` decimal(10,2) DEFAULT 0, + `VitaminB1` decimal(10,2) DEFAULT 0, + `VitaminB2` decimal(10,2) DEFAULT 0, + `Niacin` decimal(10,2) DEFAULT 0, + `VitaminC` decimal(10,2) DEFAULT 0, + `Nutrient_Volume` decimal(10,2) DEFAULT 0, + `Nutrient_Volume_Unit` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_nutrition_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_nutrition_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `fb_menu_item_ingredient` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Ingredient_id` int NOT NULL, + `Ingredient_Volume` decimal(10,2) NOT NULL, + `Ingredient_Volume_Unit` varchar(50) NOT NULL, + `Allergy_Substance_ID` int, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_id` (`FB_menu_id`), + KEY `FK_Ingredient_id` (`Ingredient_id`), + CONSTRAINT `FK_FB_menu_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE, + CONSTRAINT `FK_Ingredient_id` FOREIGN KEY (`Ingredient_id`) REFERENCES `ingredient` (`Ingredient_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `fb_menu_and_outlet_relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `FB_outlet_id` int NOT NULL, + `FB_menu_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_rel_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_rel_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO fb_menu (FB_id, FB_menu_id, Menu_Item_Description, created_at) VALUES +(1, 101, '香煎三文鱼配时蔬', NOW()), +(1, 102, '黑椒牛排配蘑菇酱', NOW()), +(1, 103, '泰式青咖喱鸡', NOW()), +(1, 104, '意式肉酱面配帕玛森芝士', NOW()), +(1, 105, '地中海风味沙拉', NOW()), +(2, 201, '清蒸鲈鱼配姜葱', NOW()), +(2, 202, '红烧狮子头', NOW()), +(2, 203, '宫保鸡丁配花生米', NOW()); + +-- 插入营养信息数据 +INSERT INTO fb_menu_item_nutrition (FB_menu_id, Calories, Protein, Fat, Carbohydrate, Sodium, Potassium, Calcium, Phosphorus, Iron, VitaminA, VitaminB1, VitaminB2, Niacin, VitaminC, Nutrient_Volume, Nutrient_Volume_Unit, created_at) VALUES + +(101, 275.00, 25.00, 15.00, 10.00, 150.00, 450.00, 30.00, 250.00, 1.80, 120.00, 0.15, 0.20, 5.00, 15.00, 350.00, 'g', NOW()), + + +(102, 320.00, 30.00, 20.00, 5.00, 200.00, 500.00, 20.00, 200.00, 3.50, 50.00, 0.10, 0.15, 7.00, 5.00, 300.00, 'g', NOW()), + + +(103, 310.00, 22.00, 18.00, 15.00, 300.00, 400.00, 50.00, 180.00, 2.00, 80.00, 0.12, 0.18, 6.00, 20.00, 350.00, 'g', NOW()), + + +(104, 480.00, 20.00, 15.00, 65.00, 800.00, 350.00, 150.00, 200.00, 2.50, 100.00, 0.20, 0.25, 4.00, 10.00, 400.00, 'g', NOW()), + + +(105, 200.00, 8.00, 12.00, 15.00, 120.00, 300.00, 100.00, 120.00, 1.20, 150.00, 0.08, 0.12, 2.00, 45.00, 250.00, 'g', NOW()); + + +INSERT INTO ingredient (Ingredient_id, Ingredient_Name, created_at) VALUES +(1, '三文鱼', NOW()), +(2, '橄榄油', NOW()), +(3, '西兰花', NOW()), +(4, '胡萝卜', NOW()), +(5, '牛排', NOW()), +(6, '黑胡椒', NOW()), +(7, '蘑菇', NOW()), +(8, '鸡胸肉', NOW()), +(9, '青咖喱酱', NOW()), +(10, '椰奶', NOW()), +(11, '意大利面', NOW()), +(12, '番茄', NOW()), +(13, '牛肉碎', NOW()), +(14, '帕玛森芝士', NOW()), +(15, '生菜', NOW()), +(16, '黄瓜', NOW()), +(17, '橄榄', NOW()), +(18, '羊奶芝士', NOW()); + + +INSERT INTO fb_menu_item_ingredient (FB_menu_id, Ingredient_id, Ingredient_Volume, Ingredient_Volume_Unit, Allergy_Substance_ID, created_at) VALUES + +(101, 1, 200.00, 'g', 7, NOW()), -- 三文鱼 (鱼类过敏 +(101, 2, 15.00, 'ml', NULL, NOW()), +(101, 3, 100.00, 'g', NULL, NOW()), +(101, 4, 50.00, 'g', NULL, NOW()), + + +(102, 5, 250.00, 'g', NULL, NOW()), +(102, 6, 5.00, 'g', NULL, NOW()), +(102, 7, 100.00, 'g', NULL, NOW()), +(102, 2, 10.00, 'ml', NULL, NOW()), + + +(103, 8, 180.00, 'g', NULL, NOW()), +(103, 9, 30.00, 'g', 5, NOW()), -- 咖喱酱 (可能含大豆) +(103, 10, 150.00, 'ml', 3, NOW()), -- 椰奶 (可能含乳制品) +(103, 3, 80.00, 'g', NULL, NOW()), + + +(104, 11, 120.00, 'g', 6, NOW()), -- 意大利面 (含麦麸) +(104, 12, 150.00, 'g', NULL, NOW()), +(104, 13, 100.00, 'g', NULL, NOW()), +(104, 14, 20.00, 'g', 3, NOW()), -- 帕玛森芝士 (含乳制品) + + +(105, 15, 100.00, 'g', NULL, NOW()), +(105, 16, 80.00, 'g', NULL, NOW()), +(105, 17, 30.00, 'g', NULL, NOW()), +(105, 18, 40.00, 'g', 3, NOW()), -- 羊奶芝士 (含乳制品) +(105, 2, 15.00, 'ml', NULL, NOW()); + + +INSERT INTO fb_menu_and_outlet_relationship (FB_id, FB_outlet_id, FB_menu_id, created_at) VALUES +(1, 1001, 101, NOW()), +(1, 1001, 102, NOW()), +(1, 1001, 103, NOW()), +(1, 1002, 101, NOW()), +(1, 1002, 104, NOW()), +(1, 1002, 105, NOW()), +(2, 2001, 201, NOW()), +(2, 2001, 202, NOW()), +(2, 2001, 203, NOW()), +(2, 2002, 201, NOW()), +(2, 2002, 202, NOW()); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/47_alter_muti_table.sql b/bin/main/db/changelog/changes/enson_change/47_alter_muti_table.sql new file mode 100644 index 0000000..64b8088 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/47_alter_muti_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +INSERT INTO fb_operator (FB_id, FB_name, FB_type, FB_cuisine, created_at) VALUES +(1, 'Healthy Eater', 'Fast Food', 'Chinese', NOW()), +(2, 'Seafood World', 'Dinner', 'Cantonese', NOW()), +*/ + +INSERT INTO fb_outlets (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) VALUES + +(1001, 1, 'Central', 'Central Financial Street 88, 1st Floor', '2345-6789', NOW()), +(1002, 1, 'Causeway Bay', 'Causeway Bay Times Square 2nd Floor', '2345-6790', NOW()), + + +(2001, 2, 'Tsim Sha Tsui', 'Tsim Sha Tsui Guangdong Road 30', '3456-7890', NOW()), +(2002, 2, 'Sha Tin', 'Sha Tin New City Plaza 5th Floor', '3456-7891', NOW()); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/48_build_muti_table.sql b/bin/main/db/changelog/changes/enson_change/48_build_muti_table.sql new file mode 100644 index 0000000..ab373ea --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/48_build_muti_table.sql @@ -0,0 +1,85 @@ +--liquibase formatted sql +--changeset terence:create-organization-tables +--comment: Create organization related tables + + +DROP TABLE daily_nutrition_summary; + + +CREATE TABLE User_Group_Organization ( + User_Grp_Org_ID INT AUTO_INCREMENT PRIMARY KEY, + User_Grp_Org_Name VARCHAR(100) NOT NULL, + Creator_User_ID INT NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + + +CREATE TABLE User_Group_Members ( + User_Grp_Org_ID INT NOT NULL, + User_ID INT NOT NULL, + R_Ship_Creator VARCHAR(50), + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_Grp_Org_ID, User_ID), + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE TABLE User_Group_Admin ( + User_Grp_Org_ID INT NOT NULL, + Admin_User_ID INT NOT NULL, + Organization_Role VARCHAR(50) NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_Grp_Org_ID, Admin_User_ID), + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE TABLE User_Delegation ( + User_ID INT NOT NULL, + Delegate_To_User_ID INT NOT NULL, + Data_Group_ID INT NOT NULL, + Delegation_Start_Date DATE NOT NULL, + Delegation_Expiry_Date DATE NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_ID, Delegate_To_User_ID, Data_Group_ID) +); + + +CREATE TABLE daily_nutrition_summary ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + User_Grp_Org_ID INT, + date DATE NOT NULL, + visibility_level INT DEFAULT 1, + data_category VARCHAR(50) DEFAULT 'regular', + reference_group_id INT, + health_metrics_reference_id INT, + total_calories DECIMAL(10,2) DEFAULT 0, + total_protein DECIMAL(10,2) DEFAULT 0, + total_fat DECIMAL(10,2) DEFAULT 0, + total_carbs DECIMAL(10,2) DEFAULT 0, + total_sodium DECIMAL(10,2) DEFAULT 0, + total_potassium DECIMAL(10,2) DEFAULT 0, + total_calcium DECIMAL(10,2) DEFAULT 0, + total_phosphorus DECIMAL(10,2) DEFAULT 0, + total_iron DECIMAL(10,2) DEFAULT 0, + total_vitamin_a DECIMAL(10,2) DEFAULT 0, + total_vitamin_b1 DECIMAL(10,2) DEFAULT 0, + total_vitamin_b2 DECIMAL(10,2) DEFAULT 0, + total_niacin DECIMAL(10,2) DEFAULT 0, + total_vitamin_c DECIMAL(10,2) DEFAULT 0, + breakfast_calories DECIMAL(10,2) DEFAULT 0, + lunch_calories DECIMAL(10,2) DEFAULT 0, + dinner_calories DECIMAL(10,2) DEFAULT 0, + snack_calories DECIMAL(10,2) DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE INDEX idx_daily_nutrition_user_date ON daily_nutrition_summary(user_id, date); +CREATE INDEX idx_daily_nutrition_org_date ON daily_nutrition_summary(User_Grp_Org_ID, date); + + + diff --git a/bin/main/db/changelog/changes/enson_change/49_alter_muti_table.sql b/bin/main/db/changelog/changes/enson_change/49_alter_muti_table.sql new file mode 100644 index 0000000..7f1981e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/49_alter_muti_table.sql @@ -0,0 +1,71 @@ +--liquibase formatted sql +--changeset terence:create-organization-tables +--comment: Create organization related tables + + + +INSERT INTO User_Group_Organization (User_Grp_Org_ID, User_Grp_Org_Name, Creator_User_ID, Creation_Date_Time) +VALUES +(1, 'Aschool', 1001, NOW()), +(2, 'Bschool', 1002, NOW()), +(3, 'Cschool', 1003, NOW()), +(4, 'Dschool', 1004, NOW()), +(5, 'Eschool', 1005, NOW()); + + +INSERT INTO User_Group_Members (User_Grp_Org_ID, User_ID, R_Ship_Creator, Creation_Date_Time) +VALUES + +(1, 1001, 'Creator', NOW()), +(1, 1010, 'Student', NOW()), +(1, 1011, 'Student', NOW()), +(1, 1012, 'Teacher', NOW()), +(1, 1013, 'Teacher', NOW()), + +(2, 1002, 'Creator', NOW()), +(2, 1020, 'Student', NOW()), +(2, 1021, 'Student', NOW()), +(2, 1022, 'Teacher', NOW()), + +(3, 1003, 'Creator', NOW()), +(3, 1030, 'Student', NOW()), +(3, 1031, 'Student', NOW()), + +(4, 1004, 'Creator', NOW()), +(4, 1040, 'Student', NOW()), + +(5, 1005, 'Creator', NOW()), +(5, 1050, 'Student', NOW()); + + +INSERT INTO User_Group_Admin (User_Grp_Org_ID, Admin_User_ID, Organization_Role, Creation_Date_Time) +VALUES + +(1, 1001, 'President', NOW()), +(1, 1012, 'Dean', NOW()), +(1, 1013, 'Dean', NOW()), + +(2, 1002, 'President', NOW()), +(2, 1022, 'Dean', NOW()), + +(3, 1003, 'President', NOW()), +(3, 1031, 'President', NOW()), + +(4, 1004, 'President', NOW()), + +(5, 1005, 'President', NOW()); + + +INSERT INTO User_Delegation (User_ID, Delegate_To_User_ID, Data_Group_ID, Delegation_Start_Date, Delegation_Expiry_Date, Creation_Date_Time) +VALUES + +(1001, 1012, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), + +(1001, 1013, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 6 MONTH), NOW()), + +(1002, 1022, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), +(1002, 1022, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), + +(1003, 1031, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 3 MONTH), NOW()), + +(1010, 1011, 4, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 7 DAY), NOW()); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/50_alter_table.sql b/bin/main/db/changelog/changes/enson_change/50_alter_table.sql new file mode 100644 index 0000000..9413a3a --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/50_alter_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:insert-sample-data +--comment: Insert sample data for testing + +ALTER TABLE `fhsmscdb`.`daily_nutrition_summary` +CHANGE COLUMN `created_at` `created` datetime, +CHANGE COLUMN `updated_at` `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN `createdBy` bigint DEFAULT NULL, +ADD COLUMN `version` int NOT NULL DEFAULT '0', +ADD COLUMN `modifiedBy` bigint DEFAULT NULL, +ADD COLUMN `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/51_alter_i18n.sql b/bin/main/db/changelog/changes/enson_change/51_alter_i18n.sql new file mode 100644 index 0000000..4080519 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/51_alter_i18n.sql @@ -0,0 +1,35 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +UPDATE fb_menu SET Menu_Item_Description = CASE FB_menu_id + WHEN 101 THEN 'Pan-fried Salmon with Seasonal Vegetables' + WHEN 102 THEN 'Black Pepper Steak with Mushroom Sauce' + WHEN 103 THEN 'Thai Green Curry Chicken' + WHEN 104 THEN 'Spaghetti Bolognese with Parmesan Cheese' + WHEN 105 THEN 'Mediterranean Salad' + WHEN 201 THEN 'Steamed Sea Bass with Ginger and Spring Onion' + WHEN 202 THEN 'Braised Lion Head Meatballs' + WHEN 203 THEN 'Kung Pao Chicken with Peanuts' +END +WHERE FB_menu_id IN (101,102,103,104,105,201,202,203); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_menu', 'Menu_Item_Description', 101, 'zh', '香煎鮭魚配時蔬'), +('fb_menu', 'Menu_Item_Description', 102, 'zh', '黑椒牛排佐蘑菇醬'), +('fb_menu', 'Menu_Item_Description', 103, 'zh', '泰國青咖哩雞'), +('fb_menu', 'Menu_Item_Description', 104, 'zh', '義式肉醬麵配帕瑪森起司'), +('fb_menu', 'Menu_Item_Description', 105, 'zh', '地中海風味沙拉'), +('fb_menu', 'Menu_Item_Description', 201, 'zh', '清蒸鱸魚配薑蔥'), +('fb_menu', 'Menu_Item_Description', 202, 'zh', '紅燒獅子頭'), +('fb_menu', 'Menu_Item_Description', 203, 'zh', '宮保雞丁配花生米'); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_menu', 'Menu_Item_Description', 101, 'en', 'Pan-fried Salmon with Seasonal Vegetables'), +('fb_menu', 'Menu_Item_Description', 102, 'en', 'Black Pepper Steak with Mushroom Sauce'), +('fb_menu', 'Menu_Item_Description', 103, 'en', 'Thai Green Curry Chicken'), +('fb_menu', 'Menu_Item_Description', 104, 'en', 'Spaghetti Bolognese with Parmesan Cheese'), +('fb_menu', 'Menu_Item_Description', 105, 'en', 'Mediterranean Salad'), +('fb_menu', 'Menu_Item_Description', 201, 'en', 'Steamed Sea Bass with Ginger and Spring Onion'), +('fb_menu', 'Menu_Item_Description', 202, 'en', 'Braised Lion Head Meatballs'), +('fb_menu', 'Menu_Item_Description', 203, 'en', 'Kung Pao Chicken with Peanuts'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/51_create_i18n.sql b/bin/main/db/changelog/changes/enson_change/51_create_i18n.sql new file mode 100644 index 0000000..a34fb27 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/51_create_i18n.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +CREATE TABLE `I18n` ( + `id` int NOT NULL AUTO_INCREMENT, + `table_name` varchar(50) NOT NULL, + `field_name` varchar(50) NOT NULL , + `record_id` int NOT NULL, + `language` varchar(10) NOT NULL, + `value` text NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `modifiedBy` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_translation` (`table_name`, `field_name`, `record_id`, `language`), + KEY `IDX_table_field` (`table_name`, `field_name`), + KEY `IDX_language` (`language`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/53_alter_i18n.sql b/bin/main/db/changelog/changes/enson_change/53_alter_i18n.sql new file mode 100644 index 0000000..d4d0b34 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/53_alter_i18n.sql @@ -0,0 +1,31 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +UPDATE fb_outlets SET FB_outlet_district = CASE id + WHEN 1 THEN 'Admiralty' + WHEN 2 THEN 'Wan Chai' + WHEN 3 THEN 'Kwun Tong' + WHEN 4 THEN 'Kwun Tong' +END +WHERE id IN (1,2,3,4); + +UPDATE fb_outlets SET FB_outlet_address = CASE id + WHEN 1 THEN 'Shop A, No.1-41, G/F, Admiralty Centre, 18 Harcourt Road' + WHEN 2 THEN 'Shop G11-17 ,G/F, C.c. Wu Building,11-17, 302-308, Hennessy Road' + WHEN 3 THEN 'Speedy Industrial Building, 2nd Floor, Room B, 114, Kwun Tong Road' + WHEN 4 THEN 'Shop No. L2-1 Level 2, apm, 418 Kwun Tong Rd' +END +WHERE id IN (1,2,3,4); +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 1, 'zh', '中環'), +('fb_outlets', 'FB_outlet_district', 2, 'zh', '灣仔'), +('fb_outlets', 'FB_outlet_district', 3, 'zh', '觀塘'), +('fb_outlets', 'FB_outlet_district', 4, 'zh', '觀塘'); + + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_address', 1, 'en', 'Shop A, No.1-41, G/F, Admiralty Centre, 18 Harcourt Road'), +('fb_outlets', 'FB_outlet_address', 2, 'en', 'Shop G11-17 ,G/F, C.c. Wu Building,11-17, 302-308, Hennessy Road'), +('fb_outlets', 'FB_outlet_address', 3, 'en', 'Speedy Industrial Building, 2nd Floor, Room B, 114, Kwun Tong Road'), +('fb_outlets', 'FB_outlet_address', 4, 'en', 'Shop No. L2-1 Level 2, apm, 418 Kwun Tong Rd'); diff --git a/bin/main/db/changelog/changes/enson_change/54_alter_i18n.sql b/bin/main/db/changelog/changes/enson_change/54_alter_i18n.sql new file mode 100644 index 0000000..e7b54b2 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/54_alter_i18n.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 1, 'en', 'Admiralty'), +('fb_outlets', 'FB_outlet_district', 2, 'en', 'Wan Chai'), +('fb_outlets', 'FB_outlet_district', 3, 'en', 'Kwun Tong'), +('fb_outlets', 'FB_outlet_district', 4, 'en', 'Kwun Tong'); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_address', 1, 'zh', '夏愨道18號海富中心地下1-41號A鋪'), +('fb_outlets', 'FB_outlet_address', 2, 'zh', '灣仔軒尼詩道302-308號皇后大廈地下G11-17號鋪'), +('fb_outlets', 'FB_outlet_address', 3, 'zh', '觀塘鯉魚門道114號迅達工業大廈2樓B室'), +('fb_outlets', 'FB_outlet_address', 4, 'zh', '觀塘鯉魚門道418號apm2樓L2-1號鋪'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/55_alter_i18n.sql b/bin/main/db/changelog/changes/enson_change/55_alter_i18n.sql new file mode 100644 index 0000000..64105b1 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/55_alter_i18n.sql @@ -0,0 +1,24 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + + + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 7, 'en', 'Central'), +('fb_outlets', 'FB_outlet_district', 8, 'en', 'Causeway Bay'), +('fb_outlets', 'FB_outlet_district', 9, 'en', 'Tsim Sha Tsui'), +('fb_outlets', 'FB_outlet_district', 10, 'en', 'Sha Tin'), +('fb_outlets', 'FB_outlet_district', 7, 'zh', '中環'), +('fb_outlets', 'FB_outlet_district', 8, 'zh', '銅鑼灣'), +('fb_outlets', 'FB_outlet_district', 9, 'zh', '尖沙咀'), +('fb_outlets', 'FB_outlet_district', 10, 'zh', '沙田'), + +('fb_outlets', 'FB_outlet_address', 7, 'en', 'Central Financial Street 88, 1st Floor'), +('fb_outlets', 'FB_outlet_address', 8, 'en', 'Causeway Bay Times Square 2nd Floor'), +('fb_outlets', 'FB_outlet_address', 9, 'en', 'Tsim Sha Tsui Guangdong Road 30'), +('fb_outlets', 'FB_outlet_address', 10, 'en', 'Sha Tin New City Plaza 5th Floor'), +('fb_outlets', 'FB_outlet_address', 7, 'zh', '金融街88號1樓'), +('fb_outlets', 'FB_outlet_address', 8, 'zh', '時代廣場2樓'), +('fb_outlets', 'FB_outlet_address', 9, 'zh', '廣東道30號'), +('fb_outlets', 'FB_outlet_address', 10, 'zh', '新城市廣場52樓'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/56_create_table.sql b/bin/main/db/changelog/changes/enson_change/56_create_table.sql new file mode 100644 index 0000000..61d17c3 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/56_create_table.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + + + +CREATE TABLE `UserAccessControl` ( + `UserId` int NOT NULL AUTO_INCREMENT, + `FunctionId` int NOT NULL, + `Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `Modified` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `CreatedBy` int DEFAULT NULL, + `ModifiedBy` int DEFAULT NULL, + PRIMARY KEY (`UserId`, `FunctionId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/58_insert_data.sql b/bin/main/db/changelog/changes/enson_change/58_insert_data.sql new file mode 100644 index 0000000..35e02d7 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/58_insert_data.sql @@ -0,0 +1,63 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', 'USER_MANAGEMENT', 'Maintain user master data'), +(2, 'VIEW_USER', 'View User', 'USER_MANAGEMENT', 'View user master data'), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', 'USER_MANAGEMENT', 'Maintain user group master data'), +(4, 'VIEW_USER_GROUP', 'View User Group', 'USER_MANAGEMENT', 'View user group master data'), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu', 'FOOD_MANAGEMENT', 'Maintain food and menu data'), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', 'FOOD_MANAGEMENT', 'View food and menu data'), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', 'FOOD_MANAGEMENT', 'Maintain food intake records'), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', 'FOOD_MANAGEMENT', 'View food intake records'), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', 'NUTRITION_MANAGEMENT', 'Maintain nutrition standards'), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', 'NUTRITION_MANAGEMENT', 'View nutrition standards'), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', 'NUTRITION_MANAGEMENT', 'Maintain nutrition analysis'), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', 'NUTRITION_MANAGEMENT', 'View nutrition analysis'), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', 'HEALTH_MANAGEMENT', 'Maintain health development data'), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', 'HEALTH_MANAGEMENT', 'View health development data'), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', 'HEALTH_MANAGEMENT', 'Maintain allergy data'), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', 'HEALTH_MANAGEMENT', 'View allergy data'), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', 'AI_MANAGEMENT', 'Maintain food AI models'), +(18, 'VIEW_AI_MODEL', 'View AI Model', 'AI_MANAGEMENT', 'View food AI models'), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', 'REPORT_MANAGEMENT', 'Generate nutrition trend analysis reports'), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', 'REPORT_MANAGEMENT', 'Generate health statistics reports'), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', 'REPORT_MANAGEMENT', 'Generate food leftover analysis reports'), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', 'FB_MANAGEMENT', 'Maintain F&B operator data'), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', 'FB_MANAGEMENT', 'View F&B operator data'), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', 'FB_MANAGEMENT', 'Maintain F&B outlet data'), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', 'FB_MANAGEMENT', 'View F&B outlet data'), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', 'ADVICE_MANAGEMENT', 'Maintain dietary advice data'), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', 'ADVICE_MANAGEMENT', 'View dietary advice data'), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', 'ACCESS_MANAGEMENT', 'Maintain access control settings'), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', 'ACCESS_MANAGEMENT', 'View access control settings'), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', 'ACCESS_MANAGEMENT', 'Maintain access delegation settings'), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', 'ACCESS_MANAGEMENT', 'View access delegation settings'); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/59_insert_data.sql b/bin/main/db/changelog/changes/enson_change/59_insert_data.sql new file mode 100644 index 0000000..9f6c03a --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/59_insert_data.sql @@ -0,0 +1,63 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/60_insert_data.sql b/bin/main/db/changelog/changes/enson_change/60_insert_data.sql new file mode 100644 index 0000000..aff3bc6 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/60_insert_data.sql @@ -0,0 +1,67 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Insert user authority mapping + +-- System Admin +INSERT INTO user_authority (userId, authId) VALUES +-- user and access control management +(2, 1), -- MAINTAIN_USER +(1, 2), -- VIEW_USER +(1, 3), -- MAINTAIN_GROUP +(1, 4), -- VIEW_USER_GROUP +(1, 28), -- MAINTAIN_ACCESS_CONTROL +(1, 29), -- VIEW_ACCESS_CONTROL +(1, 30), -- MAINTAIN_ACCESS_DELEGATION +(1, 31), -- VIEW_ACCESS_DELEGATION +-- AI和餐饮管理 +(1, 17), -- MAINTAIN_AI_MODEL +(1, 18), -- VIEW_AI_MODEL +(1, 22), -- MAINTAIN_FB_OPERATOR +(1, 23), -- VIEW_FB_OPERATOR +(1, 24), -- MAINTAIN_FB_OUTLET +(1, 25); -- VIEW_FB_OUTLET + +-- School Admin +INSERT INTO user_authority (userId, authId) VALUES +-- basic view permission +(2, 2), -- VIEW_USER +(2, 4), -- VIEW_USER_GROUP +(2, 29), -- VIEW_ACCESS_CONTROL +-- report permission +(2, 19), -- G_NUTRITION_TREND_REPORT +(2, 20), -- G_HEALTH_STATISTICS_REPORT +(2, 21), -- G_FOOD_LEFTOVER_REPORT +-- food and menu view permission +(2, 6), -- VIEW_FOOD_MENU +(2, 23), -- VIEW_FB_OPERATOR +(2, 25); -- VIEW_FB_OUTLET + +-- Student User +INSERT INTO user_authority (userId, authId) VALUES +-- personal data management +(9, 7), -- MAINTAIN_FOOD_INTAKE +(9, 8), -- VIEW_FOOD_INTAKE +(9, 13), -- MAINTAIN_HEALTH_DATA +(9, 14), -- VIEW_HEALTH_DATA +(9, 15), -- MAINTAIN_ALLERGY_DATA +(9, 16), -- VIEW_ALLERGY_DATA +-- view permission +(9, 6), -- VIEW_FOOD_MENU +(9, 10), -- VIEW_NUTRITION_STANDARD +(9, 12), -- VIEW_NUTRITION_ANALYSIS +(9, 27); -- VIEW_DIETARY_ADVICE + +-- Nutritionist/Health Advisor +INSERT INTO user_authority (userId, authId) VALUES +-- nutrition and health management +(8, 11), -- MAINTAIN_NUTRITION_ANALYSIS +(8, 12), -- VIEW_NUTRITION_ANALYSIS +(8, 26), -- MAINTAIN_DIETARY_ADVICE +(8, 27), -- VIEW_DIETARY_ADVICE +-- data view permission +(8, 14), -- VIEW_HEALTH_DATA +(8, 16), -- VIEW_ALLERGY_DATA +(8, 8), -- VIEW_FOOD_INTAKE +-- report generation +(8, 19), -- G_NUTRITION_TREND_REPORT +(8, 20); -- G_HEALTH_STATISTICS_REPORT \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/61_insert_data.sql b/bin/main/db/changelog/changes/enson_change/61_insert_data.sql new file mode 100644 index 0000000..497e21f --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/61_insert_data.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- Access Control Management +(32, 'MAINTAIN_MENU_ITEM', 'Maintain Menu Item', null, null), +(33, 'VIEW_MENU_ITEM', 'View Menu Item', null, null), +(34, 'MAINTAIN_INGREDIENT', 'Maintain Ingredient', null, null), +(35, 'VIEW_INGREDIENT', 'View Ingredient', null, null), +(36, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', null, null), +(37, 'VIEW_MENU_OUTLET', 'View Menu Outlet', null, null); diff --git a/bin/main/db/changelog/changes/enson_change/62_insert_data.sql b/bin/main/db/changelog/changes/enson_change/62_insert_data.sql new file mode 100644 index 0000000..b503cba --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/62_insert_data.sql @@ -0,0 +1,61 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +-- 插入权限数据(添加条件检查) +INSERT INTO authority (id, authority, name, module, description) +SELECT 38, 'MAINTAIN_FB_OPERATOR', 'Maintain F&B Operator', 'F&B', 'Permission to maintain F&B operator information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_FB_OPERATOR'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 39, 'VIEW_FB_OPERATOR', 'View F&B Operator', 'F&B', 'Permission to view F&B operator information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_FB_OPERATOR'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 40, 'MAINTAIN_FB_OUTLET', 'Maintain F&B Outlet', 'F&B', 'Permission to maintain F&B outlet information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_FB_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 41, 'VIEW_FB_OUTLET', 'View F&B Outlet', 'F&B', 'Permission to view F&B outlet information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_FB_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 42, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', 'F&B', 'Permission to maintain menu-outlet relationship' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_MENU_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 43, 'VIEW_MENU_OUTLET', 'View Menu Outlet', 'F&B', 'Permission to view menu-outlet relationship' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_MENU_OUTLET'); + +-- 插入用户权限关联(添加条件检查) +INSERT INTO user_authority (userId, authId) +SELECT 6, 38 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 38); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 39 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 39); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 40 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 40); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 41 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 41); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 42 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 42); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 43 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 43); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 32 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 32); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 33 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 33); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/63_insert_data.sql b/bin/main/db/changelog/changes/enson_change/63_insert_data.sql new file mode 100644 index 0000000..f8fde29 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/63_insert_data.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +-- 插入权限数据(使用INSERT IGNORE避免重复) +INSERT IGNORE INTO authority (id, authority, name, module, description) +VALUES +(38, 'MAINTAIN_FB_OPERATOR', 'Maintain F&B Operator', 'F&B', 'Permission to maintain F&B operator information'), +(39, 'VIEW_FB_OPERATOR', 'View F&B Operator', 'F&B', 'Permission to view F&B operator information'), +(40, 'MAINTAIN_FB_OUTLET', 'Maintain F&B Outlet', 'F&B', 'Permission to maintain F&B outlet information'), +(41, 'VIEW_FB_OUTLET', 'View F&B Outlet', 'F&B', 'Permission to view F&B outlet information'), +(42, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', 'F&B', 'Permission to maintain menu-outlet relationship'), +(43, 'VIEW_MENU_OUTLET', 'View Menu Outlet', 'F&B', 'Permission to view menu-outlet relationship'); + +-- 插入用户权限关联(使用INSERT IGNORE避免重复) +INSERT IGNORE INTO user_authority (userId, authId) +VALUES +(6, 38), +(6, 39), +(6, 40), +(6, 41), +(6, 42), +(6, 43), +(6, 32), +(6, 33); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/64_insert_data.sql b/bin/main/db/changelog/changes/enson_change/64_insert_data.sql new file mode 100644 index 0000000..785fb95 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/64_insert_data.sql @@ -0,0 +1,66 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); + + diff --git a/bin/main/db/changelog/changes/enson_change/65_insert_data.sql b/bin/main/db/changelog/changes/enson_change/65_insert_data.sql new file mode 100644 index 0000000..785fb95 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/65_insert_data.sql @@ -0,0 +1,66 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); + + diff --git a/bin/main/db/changelog/changes/enson_change/66_insert_data.sql b/bin/main/db/changelog/changes/enson_change/66_insert_data.sql new file mode 100644 index 0000000..bdf5207 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/66_insert_data.sql @@ -0,0 +1,16 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- Access Control Management +(32, 'MAINTAIN_MENU_ITEM', 'Maintain Menu Item', null, null), +(33, 'VIEW_MENU_ITEM', 'View Menu Item', null, null), +(34, 'MAINTAIN_INGREDIENT', 'Maintain Ingredient', null, null), +(35, 'VIEW_INGREDIENT', 'View Ingredient', null, null), +(36, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', null, null), +(37, 'VIEW_MENU_OUTLET', 'View Menu Outlet', null, null); + + + + diff --git a/bin/main/db/changelog/changes/enson_change/67_edit_table.sql b/bin/main/db/changelog/changes/enson_change/67_edit_table.sql new file mode 100644 index 0000000..4e82707 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/67_edit_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + +ALTER TABLE `FB_Operator` DROP COLUMN created_at; + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `FB_Operator` +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/bin/main/db/changelog/changes/enson_change/68_edit_table.sql b/bin/main/db/changelog/changes/enson_change/68_edit_table.sql new file mode 100644 index 0000000..7304e84 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/68_edit_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + +ALTER TABLE `fb_outlets` DROP COLUMN created_at; + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `fb_outlets` +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/bin/main/db/changelog/changes/enson_change/69_edit_table.sql b/bin/main/db/changelog/changes/enson_change/69_edit_table.sql new file mode 100644 index 0000000..5806a6e --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/69_edit_table.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `i18n` + +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/bin/main/db/changelog/changes/enson_change/70_edit_table.sql b/bin/main/db/changelog/changes/enson_change/70_edit_table.sql new file mode 100644 index 0000000..adf3ea7 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/70_edit_table.sql @@ -0,0 +1,72 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + + +INSERT INTO user_authority (userId, authId) VALUES +-- ARestaurantManager (userid 6) - 可以查看和编辑 +(6, 5), -- MAINTAIN_FOOD_MENU +(6, 6), -- VIEW_FOOD_MENU +(6, 7), -- MAINTAIN_FOOD_INTAKE +(6, 8), -- VIEW_FOOD_INTAKE +(6, 32), -- MAINTAIN_MENU_ITEM +(6, 33), -- VIEW_MENU_ITEM +(6, 34), -- MAINTAIN_INGREDIENT +(6, 35), -- VIEW_INGREDIENT +(6, 36), -- MAINTAIN_MENU_OUTLET +(6, 37), -- VIEW_MENU_OUTLET + +-- ARestaurantclerk (userid 8) - 只能查看 +(8, 6), -- VIEW_FOOD_MENU +(8, 8), -- VIEW_FOOD_INTAKE +(8, 33), -- VIEW_MENU_ITEM +(8, 35), -- VIEW_INGREDIENT +(8, 37), -- VIEW_MENU_OUTLET + +-- BRestaurantManager (userid 10) - 可以查看和编辑 +(10, 5), -- MAINTAIN_FOOD_MENU +(10, 6), -- VIEW_FOOD_MENU +(10, 7), -- MAINTAIN_FOOD_INTAKE +(10, 8), -- VIEW_FOOD_INTAKE +(10, 32), -- MAINTAIN_MENU_ITEM +(10, 33), -- VIEW_MENU_ITEM +(10, 34), -- MAINTAIN_INGREDIENT +(10, 35), -- VIEW_INGREDIENT +(10, 36), -- MAINTAIN_MENU_OUTLET +(10, 37), -- VIEW_MENU_OUTLET + +-- BRestaurantclerk (userid 11) - 只能查看 +(11, 6), -- VIEW_FOOD_MENU +(11, 8), -- VIEW_FOOD_INTAKE +(11, 33), -- VIEW_MENU_ITEM +(11, 35), -- VIEW_INGREDIENT +(11, 37), -- VIEW_MENU_OUTLET + +-- ASchoolheadmaster (userid 12) - 可以查看和编辑 +(12, 13), -- MAINTAIN_HEALTH_DATA +(12, 14), -- VIEW_HEALTH_DATA +(12, 15), -- MAINTAIN_ALLERGY_DATA +(12, 16), -- VIEW_ALLERGY_DATA +(12, 26), -- MAINTAIN_DIETARY_ADVICE +(12, 27), -- VIEW_DIETARY_ADVICE +(12, 20), -- G_HEALTH_STATISTICS_REPORT + +-- ASchoolteacher (userid 13) - 只能查看 +(13, 14), -- VIEW_HEALTH_DATA +(13, 16), -- VIEW_ALLERGY_DATA +(13, 27), -- VIEW_DIETARY_ADVICE + +-- BSchoolheadmaster (userid 14) - 可以查看和编辑 +(14, 13), -- MAINTAIN_HEALTH_DATA +(14, 14), -- VIEW_HEALTH_DATA +(14, 15), -- MAINTAIN_ALLERGY_DATA +(14, 16), -- VIEW_ALLERGY_DATA +(14, 26), -- MAINTAIN_DIETARY_ADVICE +(14, 27), -- VIEW_DIETARY_ADVICE +(14, 20), -- G_HEALTH_STATISTICS_REPORT + +-- BSchoolteacher (userid 15) - 只能查看 +(15, 14), -- VIEW_HEALTH_DATA +(15, 16), -- VIEW_ALLERGY_DATA +(15, 27); -- VIEW_DIETARY_ADVICE \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/71_edit_table.sql b/bin/main/db/changelog/changes/enson_change/71_edit_table.sql new file mode 100644 index 0000000..5ed5092 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/71_edit_table.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +INSERT INTO user_authority (userId, authId) VALUES +-- ARestaurantManager (userid 6) - 可以查看和编辑 +(6, 22), -- MAINTAIN_FB_OPERATOR +(6, 23), -- VIEW_FB_OPERATOR +(6, 24), -- MAINTAIN_FB_OUTLET +(6, 25), -- VIEW_FB_OUTLET + +-- ARestaurantclerk (userid 8) - 只能查看 +(8, 23), -- VIEW_FOOD_MENU +(8, 25), -- VIEW_FOOD_INTAKE + +-- BRestaurantManager (userid 10) - 可以查看和编辑 +(10, 22), -- MAINTAIN_FB_OPERATOR +(10, 23), -- VIEW_FB_OPERATOR +(10, 24), -- MAINTAIN_FB_OUTLET +(10, 25), -- VIEW_FB_OUTLET + +-- BRestaurantclerk (userid 11) - 只能查看 +(11, 23), -- VIEW_FOOD_MENU +(11, 25); -- VIEW_FOOD_INTAKE + diff --git a/bin/main/db/changelog/changes/enson_change/72_create_table.sql b/bin/main/db/changelog/changes/enson_change/72_create_table.sql new file mode 100644 index 0000000..a6fea54 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/72_create_table.sql @@ -0,0 +1,24 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +CREATE TABLE `fb_operator_and_organization_relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `User_Grp_Org_ID` int NOT NULL, + `created` timestamp NULL, + `createdBy` varchar(255) NULL, + `modified` timestamp NULL, + `modifiedBy` varchar(255) NULL, + `deleted` bit(1) DEFAULT 0, + `version` int DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO `fb_operator_and_organization_relationship` +(id, FB_id, User_Grp_Org_ID) VALUES +-- User Management +(1, 1, 1), +(2, 2, 2), +(3, 3, 2), +(4, 4, 1); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/73_ALTER_table.sql b/bin/main/db/changelog/changes/enson_change/73_ALTER_table.sql new file mode 100644 index 0000000..05105fa --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/73_ALTER_table.sql @@ -0,0 +1,8 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_authority +DROP PRIMARY KEY, +ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY FIRST, +ADD UNIQUE KEY unique_user_auth (userId, authId); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/74_ALTER_table.sql b/bin/main/db/changelog/changes/enson_change/74_ALTER_table.sql new file mode 100644 index 0000000..51ba038 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/74_ALTER_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_authority +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/75_ALTER_table.sql b/bin/main/db/changelog/changes/enson_change/75_ALTER_table.sql new file mode 100644 index 0000000..e661d5d --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/75_ALTER_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_group_members +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/76_ALTER_table.sql b/bin/main/db/changelog/changes/enson_change/76_ALTER_table.sql new file mode 100644 index 0000000..653520d --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/76_ALTER_table.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +-- First, drop the foreign key constraint +ALTER TABLE user_group_members +DROP FOREIGN KEY user_group_members_ibfk_1; + +-- Then drop the primary key +ALTER TABLE user_group_members +DROP PRIMARY KEY; + +-- Add the new id column +ALTER TABLE user_group_members +ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY; + +-- Re-add the foreign key constraint +ALTER TABLE user_group_members +ADD CONSTRAINT user_group_members_ibfk_1 +FOREIGN KEY (User_Grp_Org_ID) +REFERENCES user_group_organization (User_Grp_Org_ID); + +-- Add unique constraint for the original composite key +ALTER TABLE user_group_members +ADD CONSTRAINT uk_org_user UNIQUE (User_Grp_Org_ID, User_ID); \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/77_create_table.sql b/bin/main/db/changelog/changes/enson_change/77_create_table.sql new file mode 100644 index 0000000..b357104 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/77_create_table.sql @@ -0,0 +1,26 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table +ALTER TABLE fb_menu + Add category VARCHAR(50), + Add price DECIMAL(10,2), + Add is_vegetarian BOOLEAN DEFAULT FALSE, + Add is_vegan BOOLEAN DEFAULT FALSE, + Add is_gluten_free BOOLEAN DEFAULT FALSE, + Add is_spicy BOOLEAN DEFAULT FALSE + + + +--changeset yourname:004-create-menu_embedding-table +CREATE TABLE menu_item_base_embedding ( + embedding_id INT NOT NULL AUTO_INCREMENT, + FB_menu_id INT NOT NULL, + embedding_vector JSON NOT NULL, + embedding_text TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (embedding_id), + FOREIGN KEY (FB_menu_id) REFERENCES fb_menu(FB_menu_id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/78_create_table.sql b/bin/main/db/changelog/changes/enson_change/78_create_table.sql new file mode 100644 index 0000000..7fb99a6 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/78_create_table.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table +CREATE TABLE fboperator ( + id INT AUTO_INCREMENT PRIMARY KEY, + FB_name VARCHAR(255) NOT NULL, + FB_operator_address VARCHAR(255), + FB_operator_phone VARCHAR(255), + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); diff --git a/bin/main/db/changelog/changes/enson_change/79_alter_table.sql b/bin/main/db/changelog/changes/enson_change/79_alter_table.sql new file mode 100644 index 0000000..54efd95 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/79_alter_table.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table + +CREATE TABLE fb_operator_backup AS SELECT * FROM fb_operator; + + +ALTER TABLE fb_operator +ADD COLUMN FB_operator_id INT NULL AFTER FB_id; + + +ALTER TABLE fb_operator +CHANGE COLUMN FB_id id INT AUTO_INCREMENT; + +RENAME TABLE fb_operator TO fb_brand; \ No newline at end of file diff --git a/bin/main/db/changelog/changes/enson_change/80_create_table.sql b/bin/main/db/changelog/changes/enson_change/80_create_table.sql new file mode 100644 index 0000000..c1ff9f9 --- /dev/null +++ b/bin/main/db/changelog/changes/enson_change/80_create_table.sql @@ -0,0 +1,30 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table + +CREATE TABLE fb_group ( + id INT AUTO_INCREMENT PRIMARY KEY, + FB_brand_id INT NOT NULL, + FB_group_name VARCHAR(255) NOT NULL, + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); +CREATE TABLE fb_group_relationship ( + id INT AUTO_INCREMENT PRIMARY KEY, + Fb_brand_id INT NOT NULL, + FB_group_id INT NOT NULL, + FB_menu_id INT NOT NULL, + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); diff --git a/bin/main/db/changelog/db.changelog-master.yaml b/bin/main/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..b5832ba --- /dev/null +++ b/bin/main/db/changelog/db.changelog-master.yaml @@ -0,0 +1,3 @@ +databaseChangeLog: + - includeAll: + path: classpath:/db/changelog/changes \ No newline at end of file diff --git a/bin/main/log4j2-prod-linux.yml b/bin/main/log4j2-prod-linux.yml new file mode 100644 index 0000000..cfdd364 --- /dev/null +++ b/bin/main/log4j2-prod-linux.yml @@ -0,0 +1,30 @@ +Configutation: + name: Prod-Default + Properties: + Property: + name: log_location + value: /usr/springboot/logs/ + Appenders: + RollingFile: + - name: AllRollingFile_Appender + fileName: ${log_location}fhsmsc-all.log + filePattern: ${log_location}fhsmsc-all-%d{yyyy-MM-dd}.log.gz + PatternLayout: + Pattern: "%d %p [%l] - %m%n" + Policies: + TimeBasedTriggeringPolicy: + interval: 1 + modulate: true + DefaultRolloverStrategy: + Delete: + basePath: ${log_location} + maxDepth: 1 + IfFileName: + glob: fhsmsc-all-*.log.gz + IfLastModified: + age: P40D + Loggers: + Root: + level: info + AppenderRef: + - ref: AllRollingFile_Appender \ No newline at end of file diff --git a/bin/main/log4j2-prod-win.yml b/bin/main/log4j2-prod-win.yml new file mode 100644 index 0000000..e90a583 --- /dev/null +++ b/bin/main/log4j2-prod-win.yml @@ -0,0 +1,23 @@ +Configutation: + name: Prod-Default + Properties: + Property: + name: log_location + value: C:/workspace/ + Appenders: + RollingFile: + name: RollingFile_Appender + fileName: ${log_location}fhsmsc-all.log + filePattern: ${log_location}fhsmsc-all.log.%i.gz + PatternLayout: + Pattern: "%d %p [%l] - %m%n" + Policies: + SizeBasedTriggeringPolicy: + size: 4096KB + DefaultRollOverStrategy: + max: 99 + Loggers: + Root: + level: info + AppenderRef: + - ref: RollingFile_Appender \ No newline at end of file diff --git a/bin/main/log4j2.yml b/bin/main/log4j2.yml new file mode 100644 index 0000000..1d9a0cd --- /dev/null +++ b/bin/main/log4j2.yml @@ -0,0 +1,17 @@ +Configutation: + name: Default + Properties: + Property: + name: log_pattern + value: "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex" + Appenders: + Console: + name: Console_Appender + target: SYSTEM_OUT + PatternLayout: + pattern: ${log_pattern} + Loggers: + Root: + level: info + AppenderRef: + - ref: Console_Appender \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..25bb7cc --- /dev/null +++ b/build.gradle @@ -0,0 +1,69 @@ +plugins { + id 'war' + id 'java' + id 'org.springframework.boot' version '3.1.9' + id 'io.spring.dependency-management' version '1.1.0' +} + +group = 'com.ffii' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + implementation 'org.liquibase:liquibase-core' + + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' + implementation group: 'org.apache.poi', name: 'poi', version: '5.2.3' + implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.2.3' + implementation group: 'org.apache.pdfbox', name: 'pdfbox', version: '3.0.0' + + implementation group: 'jakarta.persistence', name: 'jakarta.persistence-api', version: '3.1.0' + implementation group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '2.1.1' + implementation group: 'jakarta.validation', name: 'jakarta.validation-api', version: '3.0.2' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.2' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.2' + + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' + implementation group: 'org.bytedeco', name: 'ffmpeg-platform', version: '5.1.2-1.5.8' + + implementation group: 'org.bytedeco', name: 'javacv', version: '1.5.8' + + implementation group: 'org.freemarker', name: 'freemarker', version: '2.3.32' + compileOnly group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '6.0.0' + + implementation group: 'org.apache.poi', name: 'poi', version: '5.2.2' + implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.2.2' + + implementation group: 'org.docx4j', name: 'docx4j-core', version: '11.4.11' + implementation group: 'org.docx4j', name: 'docx4j-JAXB-ReferenceImpl', version: '11.4.11' + + implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0' + implementation 'mysql:mysql-connector-java:8.0.33' + + runtimeOnly 'com.mysql:mysql-connector-j' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' +} + +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37aef8d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..a69d9cb --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f127cfd --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7d55cdf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "FHSMS-C-backend", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..cff2679 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'FhsmsC' diff --git a/src/main/java/com/ffii/core/entity/BaseEntity.java b/src/main/java/com/ffii/core/entity/BaseEntity.java new file mode 100644 index 0000000..c0281c6 --- /dev/null +++ b/src/main/java/com/ffii/core/entity/BaseEntity.java @@ -0,0 +1,129 @@ +package com.ffii.core.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Optional; + +import org.springframework.security.core.context.SecurityContextHolder; + +import com.ffii.fhsmsc.modules.user.entity.User; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Version; +import jakarta.validation.constraints.NotNull; + +/** @author Terence */ +@MappedSuperclass +public abstract class BaseEntity extends IdEntity { + + @NotNull + @Version + @Column + private Integer version; + + @NotNull + @Column(updatable = false) + private LocalDateTime created; + + @Column(updatable = false) + private Long createdBy; + + @NotNull + @Column + private LocalDateTime modified; + + @Column + private Long modifiedBy; + + @NotNull + @Column + private Boolean deleted; + + @PrePersist + public void autoSetCreated() { + this.setCreated(LocalDateTime.now()); + this.setModified(LocalDateTime.now()); + this.setDeleted(Boolean.FALSE); + + Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .ifPresentOrElse( + authentication -> { + if ("anonymousUser".equals(authentication.getPrincipal())){ + this.setCreatedBy(null); + this.setModifiedBy(null); + } else{ + Long userId = ((User)authentication.getPrincipal()).getId(); + this.setCreatedBy(userId); + this.setModifiedBy(userId); + } + }, + () -> { + this.setCreatedBy(null); + this.setModifiedBy(null); + }); + } + + @PreUpdate + public void autoSetModified() { + this.setModified(LocalDateTime.now()); + Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).ifPresentOrElse( + authentication -> this.setModifiedBy(((User)authentication.getPrincipal()).getId()), + () -> this.setModifiedBy(null)); + } + + public Integer getVersion() { + return this.version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public LocalDateTime getCreated() { + return this.created; + } + + public void setCreated(LocalDateTime created) { + this.created = created; + } + + public Long getCreatedBy() { + return this.createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getModified() { + return this.modified; + } + + public void setModified(LocalDateTime modified) { + this.modified = modified; + } + + public Long getModifiedBy() { + return this.modifiedBy; + } + + public void setModifiedBy(Long modifiedBy) { + this.modifiedBy = modifiedBy; + } + + public Boolean isDeleted() { + return this.deleted; + } + + public Boolean getDeleted() { + return this.deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/entity/IdEntity.java b/src/main/java/com/ffii/core/entity/IdEntity.java new file mode 100644 index 0000000..210fe41 --- /dev/null +++ b/src/main/java/com/ffii/core/entity/IdEntity.java @@ -0,0 +1,49 @@ +package com.ffii.core.entity; + +import java.io.Serializable; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PostLoad; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Transient; + +import org.springframework.data.domain.Persistable; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** @author Terence */ +@MappedSuperclass +public abstract class IdEntity implements Persistable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private ID id; + + @Transient + private boolean isNew = true; + + @JsonIgnore + @Override + public boolean isNew() { + return isNew; + } + + @PrePersist + @PostLoad + void markNotNew() { + this.isNew = false; + } + + // getter and setter + + public ID getId() { + return id; + } + + public void setId(ID id) { + this.id = id; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/exception/BadRequestException.java b/src/main/java/com/ffii/core/exception/BadRequestException.java new file mode 100644 index 0000000..7ac98c0 --- /dev/null +++ b/src/main/java/com/ffii/core/exception/BadRequestException.java @@ -0,0 +1,15 @@ +package com.ffii.core.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +public class BadRequestException extends ResponseStatusException { + + public BadRequestException() { + super(HttpStatus.BAD_REQUEST); + } + + public BadRequestException(String reason) { + super(HttpStatus.BAD_REQUEST, reason); + } +} diff --git a/src/main/java/com/ffii/core/exception/ConflictException.java b/src/main/java/com/ffii/core/exception/ConflictException.java new file mode 100644 index 0000000..cc1f9c5 --- /dev/null +++ b/src/main/java/com/ffii/core/exception/ConflictException.java @@ -0,0 +1,16 @@ +package com.ffii.core.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/* e.g. sub record not under record */ +public class ConflictException extends ResponseStatusException { + + public ConflictException() { + super(HttpStatus.CONFLICT); + } + + public ConflictException(String reason) { + super(HttpStatus.CONFLICT, reason); + } +} diff --git a/src/main/java/com/ffii/core/exception/InternalServerErrorException.java b/src/main/java/com/ffii/core/exception/InternalServerErrorException.java new file mode 100644 index 0000000..5587158 --- /dev/null +++ b/src/main/java/com/ffii/core/exception/InternalServerErrorException.java @@ -0,0 +1,19 @@ +package com.ffii.core.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +public class InternalServerErrorException extends ResponseStatusException { + + public InternalServerErrorException() { + super(HttpStatus.INTERNAL_SERVER_ERROR); + } + + public InternalServerErrorException(String reason) { + super(HttpStatus.INTERNAL_SERVER_ERROR, reason); + } + + public InternalServerErrorException(String reason, Throwable e) { + super(HttpStatus.INTERNAL_SERVER_ERROR, reason, e); + } +} diff --git a/src/main/java/com/ffii/core/exception/NotFoundException.java b/src/main/java/com/ffii/core/exception/NotFoundException.java new file mode 100644 index 0000000..f41d0a3 --- /dev/null +++ b/src/main/java/com/ffii/core/exception/NotFoundException.java @@ -0,0 +1,13 @@ +package com.ffii.core.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/* main record not found (e.g. item record) */ +public class NotFoundException extends ResponseStatusException{ + + public NotFoundException() { + super(HttpStatus.NOT_FOUND); + } + +} diff --git a/src/main/java/com/ffii/core/exception/UnprocessableEntityException.java b/src/main/java/com/ffii/core/exception/UnprocessableEntityException.java new file mode 100644 index 0000000..d099908 --- /dev/null +++ b/src/main/java/com/ffii/core/exception/UnprocessableEntityException.java @@ -0,0 +1,37 @@ +package com.ffii.core.exception; + +import java.util.Map; + +import jakarta.validation.constraints.NotNull; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/* sub record not found (e.g. item_line record) */ +public class UnprocessableEntityException extends ResponseStatusException { + + public UnprocessableEntityException() { + super(HttpStatus.UNPROCESSABLE_ENTITY); + } + + public UnprocessableEntityException(@NotNull Map map) { + super(HttpStatus.UNPROCESSABLE_ENTITY, map2Str(map)); + } + + public UnprocessableEntityException(String reason) { + super(HttpStatus.UNPROCESSABLE_ENTITY, reason); + } + + private static String map2Str(@NotNull Map map) { + try { + return new ObjectMapper().writeValueAsString(map); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return ""; + } + } + +} diff --git a/src/main/java/com/ffii/core/response/AuthRes.java b/src/main/java/com/ffii/core/response/AuthRes.java new file mode 100644 index 0000000..d1b96f6 --- /dev/null +++ b/src/main/java/com/ffii/core/response/AuthRes.java @@ -0,0 +1,32 @@ +package com.ffii.core.response; + +public class AuthRes { + private boolean success; + private String exception; + + public AuthRes(boolean success, String exception) { + this.success = success; + this.exception = exception; + } + + public boolean isSuccess() { + return this.success; + } + + public boolean getSuccess() { + return this.success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getException() { + return this.exception; + } + + public void setException(String exception) { + this.exception = exception; + } + +} diff --git a/src/main/java/com/ffii/core/response/DataRes.java b/src/main/java/com/ffii/core/response/DataRes.java new file mode 100644 index 0000000..d4edb54 --- /dev/null +++ b/src/main/java/com/ffii/core/response/DataRes.java @@ -0,0 +1,21 @@ +package com.ffii.core.response; + +public class DataRes { + private T data; + + public DataRes() { + } + + public DataRes(T data) { + this.data = data; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + +} diff --git a/src/main/java/com/ffii/core/response/ErrorRes.java b/src/main/java/com/ffii/core/response/ErrorRes.java new file mode 100644 index 0000000..40f2a66 --- /dev/null +++ b/src/main/java/com/ffii/core/response/ErrorRes.java @@ -0,0 +1,36 @@ +package com.ffii.core.response; + +import java.time.LocalDateTime; + +public class ErrorRes { + + private LocalDateTime timestamp; + + private String traceId; + + public ErrorRes() { + this.timestamp = LocalDateTime.now(); + } + + public ErrorRes(String traceId) { + this.timestamp = LocalDateTime.now(); + this.traceId = traceId; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public String getTraceId() { + return traceId; + } + + public void setTraceId(String traceId) { + this.traceId = traceId; + } + +} diff --git a/src/main/java/com/ffii/core/response/FailureRes.java b/src/main/java/com/ffii/core/response/FailureRes.java new file mode 100644 index 0000000..838b2b4 --- /dev/null +++ b/src/main/java/com/ffii/core/response/FailureRes.java @@ -0,0 +1,39 @@ +package com.ffii.core.response; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public class FailureRes { + + private LocalDateTime timestamp; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String error; + + public FailureRes() { + this.timestamp = LocalDateTime.now(); + } + + public FailureRes(String error) { + this.timestamp = LocalDateTime.now(); + this.error = error; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + +} diff --git a/src/main/java/com/ffii/core/response/IdRes.java b/src/main/java/com/ffii/core/response/IdRes.java new file mode 100644 index 0000000..95f72bd --- /dev/null +++ b/src/main/java/com/ffii/core/response/IdRes.java @@ -0,0 +1,21 @@ +package com.ffii.core.response; + +public class IdRes { + private long id; + + public IdRes() { + } + + public IdRes(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + +} diff --git a/src/main/java/com/ffii/core/response/RecordsRes.java b/src/main/java/com/ffii/core/response/RecordsRes.java new file mode 100644 index 0000000..7798b9e --- /dev/null +++ b/src/main/java/com/ffii/core/response/RecordsRes.java @@ -0,0 +1,41 @@ +package com.ffii.core.response; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public class RecordsRes { + private List records; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer total; + + public RecordsRes() { + } + + public RecordsRes(List records) { + this.records = records; + } + + public RecordsRes(List records, int total) { + this.records = records; + this.total = total; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + +} diff --git a/src/main/java/com/ffii/core/support/AbstractBaseEntityService.java b/src/main/java/com/ffii/core/support/AbstractBaseEntityService.java new file mode 100644 index 0000000..1a75e6e --- /dev/null +++ b/src/main/java/com/ffii/core/support/AbstractBaseEntityService.java @@ -0,0 +1,42 @@ +package com.ffii.core.support; + +import java.io.Serializable; +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import com.ffii.core.entity.BaseEntity; +import com.ffii.core.exception.ConflictException; + +/** @author Alex */ +public abstract class AbstractBaseEntityService, ID extends Serializable, R extends AbstractRepository> + extends AbstractIdEntityService { + + public AbstractBaseEntityService(JdbcDao jdbcDao, R repository) { + super(jdbcDao, repository); + } + + /** find and check versionId */ + public Optional find(ID id, int version) { + Assert.notNull(id, "id must not be null"); + return repository.findById(id) + .map(entity -> { + if (entity.getVersion() != version) throw new ConflictException("OPTIMISTIC_LOCK"); + return entity; + }); + } + + @Transactional(rollbackFor = Exception.class) + public void markDelete(ID id) { + Assert.notNull(id, "id must not be null"); + find(id).ifPresent(t -> markDelete(t)); + } + + @Transactional(rollbackFor = Exception.class) + public void markDelete(T entity) { + Assert.notNull(entity, "entity must not be null"); + entity.setDeleted(Boolean.TRUE); + save(entity); + } +} diff --git a/src/main/java/com/ffii/core/support/AbstractIdEntityService.java b/src/main/java/com/ffii/core/support/AbstractIdEntityService.java new file mode 100644 index 0000000..30612b1 --- /dev/null +++ b/src/main/java/com/ffii/core/support/AbstractIdEntityService.java @@ -0,0 +1,65 @@ +package com.ffii.core.support; + +import java.io.Serializable; +import java.util.List; +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import com.ffii.core.entity.IdEntity; + +/** @author Alex */ +public abstract class AbstractIdEntityService, ID extends Serializable, R extends AbstractRepository> + extends AbstractService { + + protected R repository; + + public AbstractIdEntityService(JdbcDao jdbcDao, R repository) { + super(jdbcDao); + this.repository = repository; + } + + @Transactional(rollbackFor = Exception.class) + public T save(T entity) { + Assert.notNull(entity, "entity must not be null"); + return this.repository.save(entity); + } + + @Transactional(rollbackFor = Exception.class) + public T saveAndFlush(T entity) { + Assert.notNull(entity, "entity must not be null"); + return this.repository.saveAndFlush(entity); + } + + public List listAll() { + return this.repository.findAll(); + } + + public Optional find(ID id) { + Assert.notNull(id, "id must not be null"); + return this.repository.findById(id); + } + + public boolean existsById(ID id) { + Assert.notNull(id, "id must not be null"); + return this.repository.existsById(id); + } + + public List findAllByIds(List ids) { + Assert.notNull(ids, "ids must not be null"); + return this.repository.findAllById(ids); + } + + @Transactional(rollbackFor = Exception.class) + public void delete(ID id) { + Assert.notNull(id, "id must not be null"); + this.repository.deleteById(id); + } + + @Transactional(rollbackFor = Exception.class) + public void delete(T entity) { + Assert.notNull(entity, "entity must not be null"); + this.repository.delete(entity); + } +} diff --git a/src/main/java/com/ffii/core/support/AbstractRepository.java b/src/main/java/com/ffii/core/support/AbstractRepository.java new file mode 100644 index 0000000..3606539 --- /dev/null +++ b/src/main/java/com/ffii/core/support/AbstractRepository.java @@ -0,0 +1,16 @@ +package com.ffii.core.support; + +import java.io.Serializable; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import com.ffii.core.entity.IdEntity; + +/** + * @author Alex + * @see https://docs.spring.io/spring-data/jpa/docs/2.7.0/reference/html/#jpa.query-methods.query-creation + */ +@NoRepositoryBean +public interface AbstractRepository, ID extends Serializable> extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/support/AbstractService.java b/src/main/java/com/ffii/core/support/AbstractService.java new file mode 100644 index 0000000..855e504 --- /dev/null +++ b/src/main/java/com/ffii/core/support/AbstractService.java @@ -0,0 +1,15 @@ +package com.ffii.core.support; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** @author Terence */ +public abstract class AbstractService { + protected final Log logger = LogFactory.getLog(getClass()); + + protected JdbcDao jdbcDao; + + public AbstractService(JdbcDao jdbcDao) { + this.jdbcDao = jdbcDao; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/support/ErrorHandler.java b/src/main/java/com/ffii/core/support/ErrorHandler.java new file mode 100644 index 0000000..c8f5c10 --- /dev/null +++ b/src/main/java/com/ffii/core/support/ErrorHandler.java @@ -0,0 +1,44 @@ +package com.ffii.core.support; + +import java.util.UUID; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.ffii.core.exception.ConflictException; +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.response.ErrorRes; +import com.ffii.core.response.FailureRes; + +@RestControllerAdvice +public class ErrorHandler extends ResponseEntityExceptionHandler { + private final Log logger = LogFactory.getLog(getClass()); + + @ExceptionHandler({ ConflictException.class, ResponseStatusException.class }) + public ResponseEntity error409422(final Exception ex) { + ResponseStatusException e = (ResponseStatusException) ex; + return new ResponseEntity<>(new FailureRes(e.getReason()), e.getStatusCode()); + } + + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity error403(final Exception ex) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + @ExceptionHandler({ InternalServerErrorException.class, Exception.class }) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity error500(final Exception ex) { + UUID traceId = UUID.randomUUID(); + logger.error("traceId: " + traceId, ex); + return new ResponseEntity<>(new ErrorRes(traceId.toString()), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/src/main/java/com/ffii/core/support/JdbcDao.java b/src/main/java/com/ffii/core/support/JdbcDao.java new file mode 100644 index 0000000..15b1841 --- /dev/null +++ b/src/main/java/com/ffii/core/support/JdbcDao.java @@ -0,0 +1,463 @@ +package com.ffii.core.support; + +import java.math.BigDecimal; +import java.sql.Blob; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.sql.DataSource; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.jdbc.BadSqlGrammarException; +import org.springframework.jdbc.IncorrectResultSetColumnCountException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +/** @author Terence */ +public class JdbcDao { + + private NamedParameterJdbcTemplate template; + + public JdbcDao(DataSource dataSource) { + this.template = new NamedParameterJdbcTemplate(dataSource); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public String queryForString(String sql) { + return this.queryForString(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public String queryForString(String sql, Map paramMap) { + try { + return this.template.queryForObject(sql, paramMap, String.class); + } catch (EmptyResultDataAccessException e) { + return StringUtils.EMPTY; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public String queryForString(String sql, Object paramObj) { + try { + return this.template.queryForObject(sql, new BeanPropertySqlParameterSource(paramObj), String.class); + } catch (EmptyResultDataAccessException e) { + return StringUtils.EMPTY; + } + } + + /** + * @return {@code true} if non-zero, {@code false} if zero + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public boolean queryForBoolean(String sql) { + return this.queryForBoolean(sql, (Map) null); + } + + /** + * @return {@code true} if non-zero, {@code false} if zero + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public boolean queryForBoolean(String sql, Map paramMap) { + try { + var rs = this.template.queryForObject(sql, paramMap, Boolean.class); + return rs == null ? false : rs; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + /** + * @return {@code true} if non-zero, {@code false} if zero + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public boolean queryForBoolean(String sql, Object paramObj) { + try { + var rs = this.template.queryForObject(sql, new BeanPropertySqlParameterSource(paramObj), Boolean.class); + return rs == null ? false : rs; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public int queryForInt(String sql) { + return this.queryForInt(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public int queryForInt(String sql, Map paramMap) { + try { + var rs = this.template.queryForObject(sql, paramMap, Integer.class); + return rs == null ? 0 : rs; + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public int queryForInt(String sql, Object paramObj) { + try { + var rs = this.template.queryForObject(sql, + new BeanPropertySqlParameterSource(paramObj), Integer.class); + return rs == null ? 0 : rs; + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public BigDecimal queryForDecimal(String sql) { + return this.queryForDecimal(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public BigDecimal queryForDecimal(String sql, Map paramMap) { + try { + return this.template.queryForObject(sql, paramMap, BigDecimal.class); + } catch (EmptyResultDataAccessException e) { + return BigDecimal.ZERO; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public BigDecimal queryForDecimal(String sql, Object paramObj) { + try { + return this.template.queryForObject(sql, + new BeanPropertySqlParameterSource(paramObj), BigDecimal.class); + } catch (EmptyResultDataAccessException e) { + return BigDecimal.ZERO; + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public Optional queryForEntity(String sql, Class entity) { + return this.queryForEntity(sql, (Map) null, entity); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public Optional queryForEntity(String sql, Map paramMap, Class entity) { + try { + return Optional.of(this.template.queryForObject(sql, paramMap, + new BeanPropertyRowMapper(entity))); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSizeDataAccessException: Incorrect result size + */ + public Optional queryForEntity(String sql, Object paramObj, Class entity) { + try { + return Optional.of(this.template.queryForObject(sql, + new BeanPropertySqlParameterSource(paramObj), new BeanPropertyRowMapper(entity))); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + /** + * @throws BadSqlGrammarException sql error + */ + public List queryForList(String sql, Class entity) { + return this.queryForList(sql, (Map) null, entity); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public List queryForList(String sql, Map paramMap, Class entity) { + return this.template.query(sql, paramMap, new BeanPropertyRowMapper(entity)); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public List queryForList(String sql, Object paramObj, Class entity) { + return this.template.query(sql, new BeanPropertySqlParameterSource(paramObj), + new BeanPropertyRowMapper(entity)); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForInts(String sql) { + return this.queryForInts(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForInts(String sql, Map paramMap) { + return this.template.queryForList(sql, paramMap, Integer.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForInts(String sql, Object paramObj) { + return this.template.queryForList(sql, new BeanPropertySqlParameterSource(paramObj), Integer.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDates(String sql) { + return this.queryForDates(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDates(String sql, Map paramMap) { + return this.template.queryForList(sql, paramMap, LocalDate.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDates(String sql, Object paramObj) { + return this.template.queryForList(sql, new BeanPropertySqlParameterSource(paramObj), LocalDate.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDatetimes(String sql) { + return this.queryForDatetimes(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDatetimes(String sql, Map paramMap) { + return this.template.queryForList(sql, paramMap, LocalDateTime.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForDatetimes(String sql, Object paramObj) { + return this.template.queryForList(sql, new BeanPropertySqlParameterSource(paramObj), LocalDateTime.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForStrings(String sql) { + return this.queryForStrings(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForStrings(String sql, Map paramMap) { + return this.template.queryForList(sql, paramMap, String.class); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + * @throws IncorrectResultSetColumnCountException Incorrect column count + */ + public List queryForStrings(String sql, Object paramObj) { + return this.template.queryForList(sql, new BeanPropertySqlParameterSource(paramObj), String.class); + } + + /** + * @throws BadSqlGrammarException sql error + */ + public List> queryForList(String sql) { + return this.queryForList(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public List> queryForList(String sql, Map paramMap) { + return this.template.queryForList(sql, paramMap); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public List> queryForList(String sql, Object paramObj) { + return this.template.queryForList(sql, new BeanPropertySqlParameterSource(paramObj)); + } + + /** + * @throws BadSqlGrammarException sql error + */ + public Optional> queryForMap(String sql) { + return this.queryForMap(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public Optional> queryForMap(String sql, Map paramMap) { + try { + return Optional.of(this.template.queryForMap(sql, paramMap)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public Optional> queryForMap(String sql, Object paramObj) { + try { + return Optional.of(this.template.queryForMap(sql, new BeanPropertySqlParameterSource(paramObj))); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + /** + * @throws BadSqlGrammarException sql error + */ + public int executeUpdate(String sql) { + return this.executeUpdate(sql, (Map) null); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public int executeUpdate(String sql, Map paramMap) { + return this.template.update(sql, paramMap); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public long executeUpdateAndReturnId(String sql, Map paramMap) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + this.template.update(sql, new MapSqlParameterSource(paramMap), keyHolder); + Number generatedId = keyHolder.getKey(); + if (generatedId != null) { + return generatedId.longValue(); + } + return 0; + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public int executeUpdate(String sql, Object paramObj) { + return this.template.update(sql, new BeanPropertySqlParameterSource(paramObj)); + } + + /** + * @throws BadSqlGrammarException sql error + * @throws InvalidDataAccessApiUsageException params missing when needed + */ + public int[] batchUpdate(String sql, List paramsMapOrObject) { + return this.template.batchUpdate(sql, SqlParameterSourceUtils.createBatch(paramsMapOrObject)); + } + + public Blob queryForBlob(String sql, Map paramMap) { + try { + return this.template.queryForObject(sql, paramMap, Blob.class); + } catch (EmptyResultDataAccessException e) { + return null; + } + } +} diff --git a/src/main/java/com/ffii/core/test/test.java b/src/main/java/com/ffii/core/test/test.java new file mode 100644 index 0000000..116ede0 --- /dev/null +++ b/src/main/java/com/ffii/core/test/test.java @@ -0,0 +1,30 @@ +package com.ffii.core.test; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class test { + private static final String JDBC_DRIVER = "your_jdbc_driver"; // Replace with your actual driver + private static final String DB_URL = "jdbc:mysql://127.0.0.1:3308/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8"; // Replace with your connection details + private static final String USER = "root"; // Replace with your database username + private static final String PASSWORD = "secret"; // Replace with your database password + + public static List getDatabaseNames() { + List databaseNames = new ArrayList<>(); + try (Connection connection = DriverManager.getConnection(DB_URL, USER, PASSWORD)) { + DatabaseMetaData metaData = connection.getMetaData(); + ResultSet rs = metaData.getCatalogs(); + while (rs.next()) { + databaseNames.add(rs.getString(1)); + } + } catch (SQLException e) { + e.printStackTrace(); + + } + return databaseNames; + } +} diff --git a/src/main/java/com/ffii/core/utils/AES.java b/src/main/java/com/ffii/core/utils/AES.java new file mode 100644 index 0000000..89830e7 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/AES.java @@ -0,0 +1,85 @@ +package com.ffii.core.utils; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class AES { + + protected final Log logger = LogFactory.getLog(getClass()); + + private static SecretKeySpec secretKey; + private static byte[] key; + + public static void setKey(String myKey) { + MessageDigest sha = null; + try { + key = myKey.getBytes("UTF-8"); + sha = MessageDigest.getInstance("SHA-1"); + key = sha.digest(key); + key = Arrays.copyOf(key, 16); + secretKey = new SecretKeySpec(key, "AES"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + public static String encrypt(String strToEncrypt, String secret) { + try { + setKey(secret); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))); + } catch (Exception e) { + System.out.println("Error while encrypting: " + e.toString()); + } + return null; + } + + public static String urlEncrypt(String strToEncrypt, String secret) { + try { + setKey(secret); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return Base64.getUrlEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))); + } catch (Exception e) { + System.out.println("Error while encrypting: " + e.toString()); + } + return null; + } + + public static String decrypt(String strToDecrypt, String secret) { + try { + setKey(secret); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)), "UTF-8"); + } catch (Exception e) { + System.out.println("Error while decrypting: " + e.toString()); + } + return null; + } + + public static String urlDecrypt(String strToDecrypt, String secret) { + try { + setKey(secret); + System.out.println("strToDecrypt: " + strToDecrypt); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return new String(cipher.doFinal(Base64.getUrlDecoder().decode(strToDecrypt)), "UTF-8"); + } catch (Exception e) { + System.out.println("Error while decrypting: " + e.toString()); + } + return null; + } +} diff --git a/src/main/java/com/ffii/core/utils/BeanUtils.java b/src/main/java/com/ffii/core/utils/BeanUtils.java new file mode 100644 index 0000000..68f6dc9 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/BeanUtils.java @@ -0,0 +1,33 @@ +package com.ffii.core.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.springframework.beans.BeansException; + +import com.ffii.core.entity.BaseEntity; + +public class BeanUtils extends org.springframework.beans.BeanUtils { + + @SuppressWarnings("unchecked") + public static void copyProperties(Object source, Object target) throws BeansException { + boolean validId = false; + Field idField = null; + Method getIdMethod = null; + + try { + idField = source.getClass().getDeclaredField("id"); + getIdMethod = source.getClass().getMethod("getId"); + + validId = idField != null && getIdMethod != null && (Long) getIdMethod.invoke(source) > 0L; + } catch (Exception e) { + + } + + if ((source instanceof BaseEntity && (((BaseEntity) source).getId() == null || ((BaseEntity) source).getId() <= 0L)) || !validId) { + org.springframework.beans.BeanUtils.copyProperties(source, target, "id"); + } else { + org.springframework.beans.BeanUtils.copyProperties(source, target); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/utils/CriteriaArgsBuilder.java b/src/main/java/com/ffii/core/utils/CriteriaArgsBuilder.java new file mode 100644 index 0000000..45778c3 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/CriteriaArgsBuilder.java @@ -0,0 +1,250 @@ +package com.ffii.core.utils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.ServletRequestUtils; + +import jakarta.servlet.http.HttpServletRequest; + +/** @author Alex */ +public class CriteriaArgsBuilder { + + private HttpServletRequest request; + private Map args; + + private CriteriaArgsBuilder(HttpServletRequest request, Map args) { + this.args = args; + this.request = request; + } + + public static CriteriaArgsBuilder withRequest(HttpServletRequest request) { + return new CriteriaArgsBuilder(request, new HashMap()); + } + + public static CriteriaArgsBuilder withRequestNMap(HttpServletRequest request, Map args) { + return new CriteriaArgsBuilder(request, args); + } + + public CriteriaArgsBuilder addStringExact(String paramName) throws ServletRequestBindingException { + String value = StringUtils.trimToNull(ServletRequestUtils.getStringParameter(this.request, paramName)); + if (value != null) + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder addStringLike(String paramName) throws ServletRequestBindingException { + String value = StringUtils.trimToNull(ServletRequestUtils.getStringParameter(this.request, paramName)); + if (value != null) + args.put(paramName, "%" + value + "%"); + return this; + } + + public CriteriaArgsBuilder addString(String paramName) throws ServletRequestBindingException { + return this.addStringExact(paramName); + } + + public CriteriaArgsBuilder addStringStartsWith(String paramName) throws ServletRequestBindingException { + String value = StringUtils.trimToNull(ServletRequestUtils.getStringParameter(this.request, paramName)); + if (value != null) + args.put(paramName, value + "%"); + return this; + } + + public CriteriaArgsBuilder addStringEndsWith(String paramName) throws ServletRequestBindingException { + String value = StringUtils.trimToNull(ServletRequestUtils.getStringParameter(this.request, paramName)); + if (value != null) + args.put(paramName, "%" + value); + return this; + } + + public CriteriaArgsBuilder addStringList(String paramName) throws ServletRequestBindingException { + String[] params = ServletRequestUtils.getStringParameters(this.request, paramName); + if (params.length > 0) { + List value = new ArrayList(params.length); + for (String param : params) + if (StringUtils.isNotBlank(param)) + value.add(param); + if (value.size() > 0) + args.put(paramName, value); + } + return this; + } + + public CriteriaArgsBuilder addStringCsv(String paramName) throws ServletRequestBindingException { + String text = ServletRequestUtils.getStringParameter(this.request, paramName); + if (text != null && StringUtils.isNotEmpty(text)) + args.put(paramName, Arrays.asList(text.split(","))); + return this; + } + + public CriteriaArgsBuilder addInteger(String paramName) throws ServletRequestBindingException { + Integer value = StringUtils.isNotBlank(this.request.getParameter(paramName)) + ? ServletRequestUtils.getRequiredIntParameter(request, paramName) + : null; + if (value != null) + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder addNonZeroInteger(String paramName) throws ServletRequestBindingException { + Integer value = StringUtils.isNotBlank(this.request.getParameter(paramName)) + ? ServletRequestUtils.getRequiredIntParameter(request, paramName) + : null; + if (value != null && value.intValue() != 0) + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder addIntegerList(String paramName) throws ServletRequestBindingException { + int[] params = ServletRequestUtils.getIntParameters(request, paramName); + if (params.length > 0) { + List values = new ArrayList(); + for (int param : params) + values.add(param); + args.put(paramName, values); + } + return this; + } + + public CriteriaArgsBuilder addIntegerListString(String paramName) throws ServletRequestBindingException { + int[] params = ServletRequestUtils.getIntParameters(request, paramName); + if (params.length > 0) { + args.put(paramName, Arrays.toString(params)); + } + return this; + } + + public CriteriaArgsBuilder addNonZeroIntegerList(String paramName) throws ServletRequestBindingException { + int[] params = ServletRequestUtils.getIntParameters(request, paramName); + if (params.length > 0) { + List values = new ArrayList(); + for (int param : params) + if (param != 0) + values.add(param); + args.put(paramName, values); + } + return this; + } + + public CriteriaArgsBuilder addLong(String paramName) throws ServletRequestBindingException { + Long value = StringUtils.isNotBlank(this.request.getParameter(paramName)) + ? ServletRequestUtils.getRequiredLongParameter(request, paramName) + : null; + if (value != null) + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder addNonZeroLong(String paramName) throws ServletRequestBindingException { + Long value = StringUtils.isNotBlank(this.request.getParameter(paramName)) + ? ServletRequestUtils.getRequiredLongParameter(request, paramName) + : null; + if (value != null && value.longValue() != 0L) + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder addDatetime(String paramName) throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDateTime.parse(value)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addDatetime(String paramName, DateTimeFormatter formatter) + throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDateTime.parse(value, formatter)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addDate(String paramName) throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDate.parse(value)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addDate(String paramName, DateTimeFormatter formatter) + throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDate.parse(value, formatter)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addDateTo(String paramName) throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDate.parse(value).plusDays(1)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addDateTo(String paramName, DateTimeFormatter formatter) + throws ServletRequestBindingException { + String value = ServletRequestUtils.getStringParameter(request, paramName); + if (StringUtils.isNotBlank(value)) { + try { + args.put(paramName, LocalDate.parse(value, formatter).plusDays(1)); + } catch (DateTimeParseException e) { + throw new ServletRequestBindingException(paramName); + } + } + return this; + } + + public CriteriaArgsBuilder addBoolean(String paramName) throws ServletRequestBindingException { + + if (request.getParameter(paramName) == null || request.getParameter(paramName).isEmpty()) { + return this; + } + Boolean value = ServletRequestUtils.getBooleanParameter(request, paramName); + args.put(paramName, value); + return this; + } + + public CriteriaArgsBuilder put(String key, Object value) { + args.put(key, value); + return this; + } + + public Map build() { + return this.args; + } +} diff --git a/src/main/java/com/ffii/core/utils/ExcelUtils.java b/src/main/java/com/ffii/core/utils/ExcelUtils.java new file mode 100644 index 0000000..b750b63 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/ExcelUtils.java @@ -0,0 +1,778 @@ +package com.ffii.core.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import jakarta.servlet.http.HttpServletResponse; + +public abstract class ExcelUtils { + + /** + * static A to Z char array + */ + private static final char[] A2Z = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z' }; + + private static final DataFormatter DATA_FORMATTER = new DataFormatter(); + + /** max rows limit of .xls **/ + public static final int MAX_ROWS = 65536; + /** max columns limit of .xls **/ + public static final int MAX_COLS = 256; + + /** + * Column reference to index (0-based) map, support up to 256 columns (compatible with .xls format) + */ + public static final Map COL_IDX = new HashMap(MAX_COLS, 1.0f); + + static { + for (int columnIndex = 0; columnIndex < MAX_COLS; columnIndex++) { + int tempColumnCount = columnIndex; + StringBuilder sb = new StringBuilder(2); + do { + sb.insert(0, A2Z[tempColumnCount % 26]); + tempColumnCount = (tempColumnCount / 26) - 1; + } while (tempColumnCount >= 0); + COL_IDX.put(sb.toString(), Integer.valueOf(columnIndex)); + } + } + + /** + * Load XSSF workbook (xlsx file) from template source. + * + * @param url + * the relative path to the template source, e.g. "WEB-INF/excel/exampleReportTemplate.xlsx" + * + * @return the workbook, or null if the template file cannot be loaded + */ + public static final Workbook loadXSSFWorkbookFromTemplateSource(ResourceLoader resourceLoader, String url) { + Resource resource = resourceLoader.getResource(url); + try { + return new XSSFWorkbook(resource.getInputStream()); + } catch (IOException e) { + return null; + } + } + + /** + * Write the workbook to byte array. + * + * @param workbook + * The Excel workbook (cannot be null) + * + * @return the byte[], or null if IO exception occurred + */ + public static final byte[] toByteArray(Workbook workbook) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + workbook.write(baos); + } catch (IOException e) { + return null; + } + return baos.toByteArray(); + } + + /** + * Check if the cell exists in the given sheet, row and column. + * + * @param sheet + * the Sheet (cannot be null) + * @param rowIndex + * 0-based row index + * @param colIndex + * 0-based column index + * + * @return {@code true} if cell exists, else {@code false} + */ + public static final boolean isCellExists(Sheet sheet, int rowIndex, int colIndex) { + Row row = sheet.getRow(rowIndex); + if (row != null) { + Cell cell = row.getCell(colIndex); + return cell != null; + } + return false; + } + + /** + * Convenient method to obtain the cell in the given sheet, row and column. + *

+ * Creates the row and the cell if not already exist. + * + * @param sheet + * the Sheet (cannot be null) + * @param rowIndex + * 0-based row index + * @param colIndex + * 0-based column index + * + * @return the Cell (never null) + */ + public static final Cell getCell(Sheet sheet, int rowIndex, int colIndex) { + Row row = sheet.getRow(rowIndex); + if (row == null) { + row = sheet.createRow(rowIndex); + } + Cell cell = row.getCell(colIndex); + if (cell == null) { + cell = row.createCell(colIndex); + } + return cell; + } + + /** + * Get column index by column reference (support up to 256 columns) + * + * @param columnRef + * column reference such as "A", "B", "AA", "AB"... + * + * @return the column index + * + * @throws NullPointerException + * if column reference is invalid or the index exceeds 256 + */ + public static final int getColumnIndex(String columnRef) { + return COL_IDX.get(columnRef); + } + + /** + * Get column reference by column index + * + * @param columnIndex + * 0-based column index + * + * @return the column reference such as "A", "B", "AA", "AB"... + */ + public static final String getColumnRef(int columnIndex) { + StringBuilder sb = new StringBuilder(); + int tempColumnCount = columnIndex; + do { + sb.insert(0, A2Z[tempColumnCount % 26]); + tempColumnCount = (tempColumnCount / 26) - 1; + } while (tempColumnCount >= 0); + return sb.toString(); + } + + /** + * Get the Excel Cell Ref String by columnIndex and rowIndex + * + * @param columnIndex + * 0-based column index + * @param rowIndex + * 0-based row index + */ + public static final String getCellRefString(int columnIndex, int rowIndex) { + StringBuilder sb = new StringBuilder(); + int tempColumnCount = columnIndex; + do { + sb.insert(0, A2Z[tempColumnCount % 26]); + tempColumnCount = (tempColumnCount / 26) - 1; + } while (tempColumnCount >= 0); + sb.append(rowIndex + 1); + return sb.toString(); + } + + /** + * Get Cell value as String + */ + public static String getStringValue(Cell cell) { + if (cell != null && cell.getCellType() == CellType.FORMULA) { + try { + return cell.getStringCellValue(); + } catch (Exception e) { + return ""; + } + } + return DATA_FORMATTER.formatCellValue(cell); + } + + /** + * Get Cell value as BigDecimal, with a fallback value + *

+ * Only support {@link CellType#NUMERIC} and {@link CellType#STRING} + * + * @return the BigDecimal value, or the default value if cell is null or cell type is {@link CellType#BLANK} + */ + public static BigDecimal getDecimalValue(Cell cell, BigDecimal defaultValue) { + if (cell == null || cell.getCellType() == CellType.BLANK) return defaultValue; + if (cell.getCellType() == CellType.STRING) { + return new BigDecimal(cell.getStringCellValue()); + } else { + return BigDecimal.valueOf(cell.getNumericCellValue()); + } + } + + /** + * Get Cell value as BigDecimal + *

+ * Only support {@link CellType#NUMERIC} and {@link CellType#STRING} + * + * @return the BigDecimal value, or BigDecimal.ZERO if cell is null or cell type is {@link CellType#BLANK} + */ + public static BigDecimal getDecimalValue(Cell cell) { + return getDecimalValue(cell, BigDecimal.ZERO); + } + + /** + * Get Cell value as double + *

+ * Only support {@link CellType#NUMERIC} and {@link CellType#STRING} + */ + public static double getDoubleValue(Cell cell) { + if (cell == null) return 0.0; + if (cell.getCellType() == CellType.STRING) { + return NumberUtils.toDouble(cell.getStringCellValue()); + } else { + return cell.getNumericCellValue(); + } + } + + /** + * Get Cell value as int (rounded half-up to the nearest integer) + *

+ * Only support {@link CellType#NUMERIC} and {@link CellType#STRING} + */ + public static int getIntValue(Cell cell) { + return BigDecimal.valueOf(getDoubleValue(cell)).setScale(0, RoundingMode.HALF_UP).intValue(); + } + + /** + * Get Cell Integer value (truncated) + */ + public static Integer getIntValue(Cell cell, Integer defaultValue) { + if (cell == null) return defaultValue; + if (cell.getCellType() == CellType.STRING) { + return NumberUtils.toInt(cell.getStringCellValue(), defaultValue); + } else { + return (int) cell.getNumericCellValue(); + } + } + + public static LocalDate getDateValue(Cell cell, DateTimeFormatter formatter) { + if (cell == null) return null; + if (cell.getCellType() == CellType.STRING) { + try { + return LocalDate.parse(cell.getStringCellValue(), formatter); + } catch (DateTimeParseException e) { + return null; + } + } + if (DateUtil.isCellDateFormatted(cell)) { + try { + return DateUtil.getJavaDate(cell.getNumericCellValue()).toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + } catch (NumberFormatException e) { + return null; + } + } else { + return null; + } + } + + public static LocalDateTime getDatetimeValue(Cell cell, DateTimeFormatter formatter) { + if (cell == null) return null; + if (cell.getCellType() == CellType.STRING) { + try { + return LocalDateTime.parse(cell.getStringCellValue(), formatter); + } catch (DateTimeParseException e) { + return null; + } + } + if (DateUtil.isCellDateFormatted(cell)) { + try { + return DateUtil.getJavaDate(cell.getNumericCellValue()).toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDateTime(); + } catch (NumberFormatException e) { + return null; + } + } else { + return null; + } + } + + /** + * Convenient method to set Cell value + * + * @param cell + * the Cell (cannot be null) + * @param value + * the value to set + */ + public static void setCellValue(Cell cell, Object value) { + if (value instanceof String) + cell.setCellValue((String) value); + else if (value instanceof RichTextString) + cell.setCellValue((RichTextString) value); + else if (value instanceof Number) + cell.setCellValue(((Number) value).doubleValue()); + else if (value instanceof Boolean) + cell.setCellValue(((Boolean) value).booleanValue()); + else if (value instanceof Calendar) + cell.setCellValue((Calendar) value); + else if (value instanceof Date) + cell.setCellValue((Date) value); + else if (value instanceof LocalDate) + cell.setCellValue((LocalDate) value); + else if (value instanceof LocalTime) + cell.setCellValue(((LocalTime) value).toString()); + else if (value instanceof LocalDateTime) + cell.setCellValue((LocalDateTime) value); + else if (value == null) + cell.setCellValue(""); + else + throw new IllegalArgumentException(value.getClass().toString() + " is not supported"); + } + + /** + * Convenient method to set Cell value by Sheet, row index, and column index + * + * @param sheet + * the Sheet (cannot be null) + * @param rowIndex + * 0-based row index + * @param colIndex + * 0-based column index + * @param value + * the value to set + */ + public static void setCellValue(Sheet sheet, int rowIndex, int colIndex, Object value) { + setCellValue(getCell(sheet, rowIndex, colIndex), value); + } + + /** + * Increase Row Height (if necessary, but never decrease it) by counting the no. of lines in a String value + * + * @param sheet + * The Excel worksheet + * @param row + * The row index (0-based) + * @param value + * The (multi-line) String value to count for the no. of lines + * @param heightInPoints + * The height (in points) for 1 line of text + */ + public static void increaseRowHeight(Sheet sheet, int row, String value, int heightInPoints) { + int lines = StringUtils.countMatches(value, "\n") + 1; // count no. of lines + float newHeight = heightInPoints * lines; + + Row r = sheet.getRow(row); + if (r == null) r = sheet.createRow(row); + + // increase the row height if necessary, but never decrease it + if (r.getHeightInPoints() < newHeight) { + r.setHeightInPoints(newHeight); + } + } + + /** + * Add merged region (i.e. merge cells) + * + * @param sheet + * The Excel worksheet + * @param firstRowIdx + * The first row index (0-based) + * @param lastRowIdx + * The last row index (0-based) + * @param firstColIdx + * The first column index (0-based) + * @param lastColIdx + * The last column index (0-based) + */ + public static void addMergedRegion(Sheet sheet, int firstRowIdx, int lastRowIdx, int firstColIdx, int lastColIdx) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRowIdx, lastRowIdx, firstColIdx, lastColIdx); + sheet.addMergedRegion(cellRangeAddress); + } + + /** + * Copy and Insert Row + * + * @param workbook + * The Excel workbook + * @param sourceSheet + * The source Excel worksheet + * @param destinationSheet + * The destination Excel worksheet + * @param sourceRowNum + * The source row index (0-based) to copy from + * @param destinationRowNum + * The destination row index (0-based) to insert into (from the copied row) + */ + public static void copyAndInsertRow(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int sourceRowNum, int destinationRowNum) { + // get the source / destination row + Row sourceRow = sourceSheet.getRow(sourceRowNum); + Row destRow = destinationSheet.getRow(destinationRowNum); + + // if the row exist in destination, push down all rows by 1 + if (destRow != null) { + destinationSheet.shiftRows(destinationRowNum, destinationSheet.getLastRowNum(), 1, true, false); + } + // create a new row + destRow = destinationSheet.createRow(destinationRowNum); + + // loop through source columns to add to new row + for (int i = 0; i < sourceRow.getLastCellNum(); i++) { + // grab a copy of the old cell + Cell oldCell = sourceRow.getCell(i); + + // if the old cell is null jump to next cell + if (oldCell == null) continue; + + // create a new cell in destination row + Cell newCell = destRow.createCell(i); + + // apply cell style to new cell from old cell + newCell.setCellStyle(oldCell.getCellStyle()); + + // if there is a cell comment, copy + if (oldCell.getCellComment() != null) { + newCell.setCellComment(oldCell.getCellComment()); + } + + // if there is a cell hyperlink, copy + if (oldCell.getHyperlink() != null) { + newCell.setHyperlink(oldCell.getHyperlink()); + } + + // copy the cell data value + switch (oldCell.getCellType()) { + case NUMERIC: + newCell.setCellValue(oldCell.getNumericCellValue()); + break; + case STRING: + newCell.setCellValue(oldCell.getRichStringCellValue()); + break; + case FORMULA: + newCell.setCellFormula(oldCell.getCellFormula()); + break; + case BLANK: + newCell.setCellValue(oldCell.getStringCellValue()); + break; + case BOOLEAN: + newCell.setCellValue(oldCell.getBooleanCellValue()); + break; + case ERROR: + newCell.setCellErrorValue(oldCell.getErrorCellValue()); + break; + default: + break; + } + } + + // if there are any merged regions in the source row, copy to new row + for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { + CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(i); + if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { + addMergedRegion( + destinationSheet, + destRow.getRowNum(), + (destRow.getRowNum() + (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())), + cellRangeAddress.getFirstColumn(), + cellRangeAddress.getLastColumn()); + } + } + + // copy row height + destRow.setHeight(sourceRow.getHeight()); + } + + /** + * Copy and Insert Row + * + * @param workbook + * The Excel workbook + * @param sheet + * The Excel worksheet + * @param sourceRowNum + * The source row index (0-based) to copy from + * @param destinationRowNum + * The destination row index (0-based) to insert into (from the copied row) + */ + public static void copyAndInsertRow(Workbook workbook, Sheet sheet, int sourceRowNum, int destinationRowNum) { + copyAndInsertRow(workbook, sheet, sheet, sourceRowNum, destinationRowNum); + } + + public static void copyAndInsertRow(Workbook workbook, Sheet sourceSheet, int sourceRowNum, int destinationRowNum, int times) { + // get the source / destination row + Row sourceRow = sourceSheet.getRow(sourceRowNum); + + Row[] destRows = new Row[times]; + for (int j = 0; j < times; j++) { + Row destRow = sourceSheet.getRow(destinationRowNum + j); + // if the row exist in destination, push down all rows by 1 + if (destRow != null) { + sourceSheet.shiftRows(destinationRowNum + j, sourceSheet.getLastRowNum(), 1, true, false); + } + // create a new row + destRows[j] = sourceSheet.createRow(destinationRowNum + j); + // copy row height + destRows[j].setHeight(sourceRow.getHeight()); + } + + // loop through source columns to add to new row + for (int i = 0; i < sourceRow.getLastCellNum(); i++) { + // grab a copy of the old cell + Cell oldCell = sourceRow.getCell(i); + + // if the old cell is null jump to next cell + if (oldCell == null) continue; + + for (int k = 0; k < times; k++) { + // create a new cell in destination row + Cell newCell = destRows[k].createCell(i); + + // apply cell style to new cell from old cell + newCell.setCellStyle(oldCell.getCellStyle()); + } + } + } + + /** + * Copy Column + * + * @param workbook + * The Excel workbook + * @param sourceSheet + * The source Excel worksheet + * @param destinationSheet + * The destination Excel worksheet + * @param rowStart + * The source row start index (0-based) to copy from + * @param rowEnd + * The source row end index (0-based) to copy from + * @param sourceColumnNum + * The source column index (0-based) to copy from + * @param destinationColumnNum + * The destination column index (0-based) to copy into (from the copied row) + */ + public static void copyColumn(Workbook workbook, Sheet sourceSheet, Sheet destinationSheet, int rowStart, int rowEnd, int sourceColumnNum, + int destinationColumnNum) { + for (int i = rowStart; i <= rowEnd; i++) { + Row sourceRow = sourceSheet.getRow(i); + if (sourceRow == null) continue; + + Row destinationRow = destinationSheet.getRow(i); + if (destinationRow == null) destinationRow = destinationSheet.createRow(i); + + Cell oldCell = sourceRow.getCell(sourceColumnNum); + if (oldCell == null) continue; + + Cell newCell = destinationRow.createCell(destinationColumnNum); + + newCell.setCellStyle(oldCell.getCellStyle()); + + if (oldCell.getCellComment() != null) { + newCell.setCellComment(oldCell.getCellComment()); + } + + if (oldCell.getHyperlink() != null) { + newCell.setHyperlink(oldCell.getHyperlink()); + } + + switch (oldCell.getCellType()) { + case NUMERIC: + newCell.setCellValue(oldCell.getNumericCellValue()); + break; + case STRING: + newCell.setCellValue(oldCell.getRichStringCellValue()); + break; + case FORMULA: + newCell.setCellFormula(oldCell.getCellFormula()); + break; + case BLANK: + newCell.setCellValue(oldCell.getStringCellValue()); + break; + case BOOLEAN: + newCell.setCellValue(oldCell.getBooleanCellValue()); + break; + case ERROR: + newCell.setCellErrorValue(oldCell.getErrorCellValue()); + break; + default: + break; + } + + for (int ii = 0; ii < sourceSheet.getNumMergedRegions(); ii++) { + CellRangeAddress cellRangeAddress = sourceSheet.getMergedRegion(ii); + if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) { + addMergedRegion( + destinationSheet, + cellRangeAddress.getFirstRow(), + cellRangeAddress.getLastRow(), + destinationColumnNum, + (destinationColumnNum + (cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn()))); + } + } + } + + destinationSheet.setColumnWidth(destinationColumnNum, sourceSheet.getColumnWidth(sourceColumnNum)); + } + + /** + * Copy Column + * + * @param workbook + * The Excel workbook + * @param sheet + * The Excel worksheet + * @param rowStart + * The source row start index (0-based) to copy from + * @param rowEnd + * The source row end index (0-based) to copy from + * @param sourceColumnNum + * The source column index (0-based) to copy from + * @param destinationColumnNum + * The destination column index (0-based) to copy into (from the copied row) + */ + public static void copyColumn(Workbook workbook, Sheet sheet, int rowStart, int rowEnd, int sourceColumnNum, int destinationColumnNum) { + copyColumn(workbook, sheet, sheet, rowStart, rowEnd, sourceColumnNum, destinationColumnNum); + } + + public static void shiftColumns(Row row, int startingIndex, int shiftCount) { + for (int i = row.getPhysicalNumberOfCells() - 1; i >= startingIndex; i--) { + Cell oldCell = row.getCell(i); + Cell newCell = row.createCell(i + shiftCount); + + // apply cell style to new cell from old cell + newCell.setCellStyle(oldCell.getCellStyle()); + + // if there is a cell comment, copy + if (oldCell.getCellComment() != null) { + newCell.setCellComment(oldCell.getCellComment()); + } + + // if there is a cell hyperlink, copy + if (oldCell.getHyperlink() != null) { + newCell.setHyperlink(oldCell.getHyperlink()); + } + + // copy the cell data value + switch (oldCell.getCellType()) { + case NUMERIC: + newCell.setCellValue(oldCell.getNumericCellValue()); + break; + case STRING: + newCell.setCellValue(oldCell.getRichStringCellValue()); + break; + case FORMULA: + newCell.setCellFormula(oldCell.getCellFormula()); + break; + case BLANK: + newCell.setCellValue(oldCell.getStringCellValue()); + break; + case BOOLEAN: + newCell.setCellValue(oldCell.getBooleanCellValue()); + break; + case ERROR: + newCell.setCellErrorValue(oldCell.getErrorCellValue()); + break; + default: + break; + } + } + } + + /** handle some invalid char included ( /\*[]:? ) */ + public static void setSheetName(Workbook workbook, Sheet sheet, String name) { + if (workbook != null && sheet != null && StringUtils.isNotBlank(name)) + workbook.setSheetName(workbook.getSheetIndex(sheet), name.replaceAll("[/\\\\*\\[\\]:\\?]", "_")); + } + + /** delete row */ + public static void deleteRow(Sheet sheet, int rowIndex) { + if (sheet != null) { + sheet.removeRow(sheet.getRow(rowIndex)); + if (rowIndex < sheet.getLastRowNum()) + sheet.shiftRows(rowIndex, sheet.getLastRowNum(), -1); + } + } + + public static byte[] encrypt(Workbook workbook, String password) { + return encrypt(toByteArray(workbook), password); + } + + public static byte[] encrypt(byte[] bytes, String password) { + try { + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); + // EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null); + + Encryptor enc = info.getEncryptor(); + enc.confirmPassword(password); + + // Read in an existing OOXML file and write to encrypted output stream + // don't forget to close the output stream otherwise the padding bytes aren't added + OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(bytes)); + OutputStream os = enc.getDataStream(fs); + opc.save(os); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + fs.writeFilesystem(bos); + + return bos.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Workbook loadTemplate(String templateClasspath) throws InvalidFormatException, IOException { + return loadTemplateFile(templateClasspath); + } + + public static Workbook loadTemplateFile(String templateClasspath) throws InvalidFormatException, IOException { + ClassPathResource r = new ClassPathResource(templateClasspath + "_" + ".xlsx"); + if (!r.exists()) r = new ClassPathResource(templateClasspath + ".xlsx"); + + try (InputStream in = r.getInputStream()) { + return new XSSFWorkbook(in); + } + } + + public static void send(HttpServletResponse response, Workbook workbook, String filename) throws IOException { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", + response.encodeURL(filename + ".xlsx"))); + try (OutputStream out = response.getOutputStream()) { + workbook.write(out); + } + } +} diff --git a/src/main/java/com/ffii/core/utils/FileUtils.java b/src/main/java/com/ffii/core/utils/FileUtils.java new file mode 100644 index 0000000..f5c89a6 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/FileUtils.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright 2013 2Fi Business Solutions Ltd. + * + * This code is part of the Core project. + * + * This code is copyrighted. Under no circumstances should any party, people, + * or organization should redistribute any portions of this code in any form, + * either verbatim or through electronic media, to any third parties, unless + * under explicit written permission by 2Fi Business Solutions Ltd. + ******************************************************************************/ +package com.ffii.core.utils; + +import java.util.HashMap; +import java.util.Map; + +/** + * File Utils + * + * @author Patrick + */ +public abstract class FileUtils { + + private static final Map MIMETYPES = new HashMap<>(); + + static { + MIMETYPES.put("pdf", "application/pdf"); + + MIMETYPES.put("doc", "application/msword"); + MIMETYPES.put("dot", "application/msword"); + MIMETYPES.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + + MIMETYPES.put("xls", "application/vnd.ms-excel"); + MIMETYPES.put("xlm", "application/vnd.ms-excel"); + MIMETYPES.put("xla", "application/vnd.ms-excel"); + MIMETYPES.put("xlc", "application/vnd.ms-excel"); + MIMETYPES.put("xlt", "application/vnd.ms-excel"); + MIMETYPES.put("xlw", "application/vnd.ms-excel"); + MIMETYPES.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + + MIMETYPES.put("ppt", "application/vnd.ms-powerpoint"); + MIMETYPES.put("pps", "application/vnd.ms-powerpoint"); + MIMETYPES.put("pot", "application/vnd.ms-powerpoint"); + MIMETYPES.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + + MIMETYPES.put("bat", "application/x-msdownload"); + MIMETYPES.put("com", "application/x-msdownload"); + MIMETYPES.put("dll", "application/x-msdownload"); + MIMETYPES.put("exe", "application/x-msdownload"); + MIMETYPES.put("msi", "application/x-msdownload"); + + MIMETYPES.put("swf", "application/x-shockwave-flash"); + + MIMETYPES.put("7z", "application/x-7z-compressed"); + MIMETYPES.put("rar", "application/x-rar-compressed"); + MIMETYPES.put("zip", "application/zip"); + + MIMETYPES.put("js", "application/javascript"); + MIMETYPES.put("json", "application/json"); + + MIMETYPES.put("mpga", "audio/mpeg"); + MIMETYPES.put("mp2", "audio/mpeg"); + MIMETYPES.put("mp2a", "audio/mpeg"); + MIMETYPES.put("mp3", "audio/mpeg"); + MIMETYPES.put("m2a", "audio/mpeg"); + MIMETYPES.put("m3a", "audio/mpeg"); + + MIMETYPES.put("bmp", "image/bmp"); + MIMETYPES.put("gif", "image/gif"); + MIMETYPES.put("jpeg", "image/jpeg"); + MIMETYPES.put("jpg", "image/jpeg"); + MIMETYPES.put("jpe", "image/jpeg"); + MIMETYPES.put("png", "image/png"); + MIMETYPES.put("tiff", "image/tiff"); + MIMETYPES.put("tif", "image/tiff"); + MIMETYPES.put("avif", "image/avif"); + + + MIMETYPES.put("css", "text/css"); + + MIMETYPES.put("csv", "text/csv"); + + MIMETYPES.put("html", "text/html"); + MIMETYPES.put("htm", "text/html"); + + MIMETYPES.put("txt", "text/plain"); + MIMETYPES.put("text", "text/plain"); + MIMETYPES.put("conf", "text/plain"); + MIMETYPES.put("log", "text/plain"); + + MIMETYPES.put("mp4", "video/mp4"); + MIMETYPES.put("mp4v", "video/mp4"); + MIMETYPES.put("mpg4", "video/mp4"); + + MIMETYPES.put("mkv", "video/x-matroska"); + + MIMETYPES.put("mpeg", "video/mpeg"); + MIMETYPES.put("mpg", "video/mpeg"); + MIMETYPES.put("mpe", "video/mpeg"); + MIMETYPES.put("m1v", "video/mpeg"); + MIMETYPES.put("m2v", "video/mpeg"); + + MIMETYPES.put("qt", "video/quicktime"); + MIMETYPES.put("mov", "video/quicktime"); + + MIMETYPES.put("wmv", "video/x-ms-wmv"); + MIMETYPES.put("wmx", "video/x-ms-wmx"); + MIMETYPES.put("wvx", "video/x-ms-wvx"); + MIMETYPES.put("avi", "video/x-msvideo"); + + // MIMETYPES.put("xxxxx", "xxxxx"); + } + + /** + * Guess the mimetype from the file name extension + * + * @return The mimetype guessed from the file name extension, or {@code null} if the mimetype cannot be determined + */ + public static String guessMimetype(String filename) { + String extension = StringUtils.substringAfterLast(filename, "."); + extension = extension.toLowerCase(); + String mimetype = MIMETYPES.get(extension); + return mimetype != null ? mimetype : "application/octet-stream"; + } + +} diff --git a/src/main/java/com/ffii/core/utils/JsonUtils.java b/src/main/java/com/ffii/core/utils/JsonUtils.java new file mode 100644 index 0000000..4efd35a --- /dev/null +++ b/src/main/java/com/ffii/core/utils/JsonUtils.java @@ -0,0 +1,47 @@ +package com.ffii.core.utils; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +/** + * JSON Utils + * + * @author Patrick + */ +public abstract class JsonUtils { + + // Default mapper instance + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * Method that can be used to serialize any Java value as a JSON String. + */ + public static String toJsonString(Object obj) { + try { + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return mapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + return null; + } + } + + /** + * Read from JSON String. + * + * @param content + * JSON String content + * @param valueType + * the return type + */ + public static T fromJsonString(String content, Class valueType) throws JsonParseException, JsonMappingException, IOException { + return mapper.readValue(content, valueType); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/core/utils/JwtTokenUtil.java b/src/main/java/com/ffii/core/utils/JwtTokenUtil.java new file mode 100644 index 0000000..490c985 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/JwtTokenUtil.java @@ -0,0 +1,114 @@ +package com.ffii.core.utils; + +import java.io.Serializable; +import java.security.Key; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import com.ffii.fhsmsc.model.RefreshToken; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +@Component +@Scope(value = ConfigurableBeanFactory. SCOPE_SINGLETON) +public class JwtTokenUtil implements Serializable { + + Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class); + + private static final long serialVersionUID = -2550185165626007488L; + + // * 60000 = 1 Min + public static final long JWT_REFRESH_TOKEN_EXPIRED_TIME = 60000 * 3600; + public static final String AES_SECRET = "ffii"; + public static final String TOKEN_SEPARATOR = "@@"; + + // @Value("${jwt.secret}") + // private String secret; + + private static final Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512); + + // retrieve username from jwt token + public String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + // retrieve expiration date from jwt token + public Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + // for retrieveing any information from token we will need the secret key + private Claims getAllClaimsFromToken(String token) { + return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody(); + } + + // check if the token has expired + private Boolean isTokenExpired(String token) { + final Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + // generate token for user + public String generateToken(UserDetails userDetails, long accessTokenExpiry) { + Map claims = new HashMap<>(); + return doGenerateToken(claims, userDetails.getUsername(), accessTokenExpiry); + } + + // while creating the token - + // 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID + // 2. Sign the JWT using the HS512 algorithm and secret key. + // 3. According to JWS Compact + // Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) + // compaction of the JWT to a URL-safe string + private String doGenerateToken(Map claims, String subject, long accessTokenExpiry) { + logger.info((new Date(System.currentTimeMillis() + accessTokenExpiry)).toString()); + return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + accessTokenExpiry)) + .signWith(secretKey).compact(); + } + + // validate token + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = getUsernameFromToken(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + public RefreshToken createRefreshToken(String username) { + RefreshToken refreshToken = new RefreshToken(); + refreshToken.setUserName(username); + refreshToken.setExpiryDate(Instant.now().plusMillis(JWT_REFRESH_TOKEN_EXPIRED_TIME)); + long instantNum = Instant.now().plusMillis(JWT_REFRESH_TOKEN_EXPIRED_TIME).toEpochMilli(); + refreshToken.setToken(AES.encrypt(username + TOKEN_SEPARATOR + instantNum, AES_SECRET)); + return refreshToken; + } + + public boolean verifyExpiration(RefreshToken token) throws Exception { + if (token.getExpiryDate().compareTo(Instant.now()) < 0) { + return false; + } + + return true; + } + + public String getUsernameFromRefreshToken(String refreshToken) { + return AES.decrypt(refreshToken, AES_SECRET); + } +} diff --git a/src/main/java/com/ffii/core/utils/LocaleUtils.java b/src/main/java/com/ffii/core/utils/LocaleUtils.java new file mode 100644 index 0000000..3964259 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/LocaleUtils.java @@ -0,0 +1,42 @@ +package com.ffii.core.utils; + +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.i18n.LocaleContextHolder; + +/** this utils follow "-" standard ("zh-TW", no "zh_TW") */ +public abstract class LocaleUtils { + + public static Locale getLocale() { + return LocaleContextHolder.getLocale(); + } + + public static String getLocaleStr() { + return toLocaleStr(LocaleContextHolder.getLocale()); + } + + public static String toLocaleStr(Locale locale) { + String language = locale.getLanguage(); + String country = locale.getCountry(); + + if (StringUtils.isNotBlank(country)) { + return language + "-" + country; + } else { + return language; + } + } + + /** + * @param localeStr + * e.g. zh-TW + */ + public static Locale from(String localeStr) { + String[] localeArr = localeStr.split("-"); + if (localeArr.length == 1) { + return new Locale(localeArr[0]); + } else { + return new Locale(localeArr[0], localeArr[1]); + } + } +} diff --git a/src/main/java/com/ffii/core/utils/MapUtils.java b/src/main/java/com/ffii/core/utils/MapUtils.java new file mode 100644 index 0000000..733bda9 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/MapUtils.java @@ -0,0 +1,35 @@ +package com.ffii.core.utils; + +import java.util.HashMap; +import java.util.Map; + +/** + * MapUtils + * + * @author Patrick + */ +public class MapUtils { + + /** + * Convert key-value pairs to HashMap + * + * @param keyValuePairs + * Keys and values must be in pairs + * + * @return Map + */ + @SuppressWarnings("unchecked") + public static Map toHashMap(Object... keyValuePairs) { + if (keyValuePairs.length % 2 != 0) + throw new IllegalArgumentException("Keys and values must be in pairs"); + + Map map = new HashMap(keyValuePairs.length / 2); + + for (int i = 0; i < keyValuePairs.length; i += 2) { + map.put((K) keyValuePairs[i], (V) keyValuePairs[i + 1]); + } + + return map; + } + +} diff --git a/src/main/java/com/ffii/core/utils/NumberUtils.java b/src/main/java/com/ffii/core/utils/NumberUtils.java new file mode 100644 index 0000000..7027ff6 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/NumberUtils.java @@ -0,0 +1,45 @@ +package com.ffii.core.utils; + +public class NumberUtils extends org.apache.commons.lang3.math.NumberUtils{ + + private static final String[] units = { "", "one", "two", "three", "four", + "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", + "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", + "eighteen", "nineteen" }; + + private static final String[] tens = { + "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", + "eighty", "ninety" }; + + public static String convertToWord(int number) { + if (number == 0) { + return "zero"; + } + + if (number < 0) { + return "minus " + convertToWord(-number); + } + + if (number < 20) { + return units[number]; + } + + if (number < 100) { + return tens[number / 10] + ((number % 10 != 0) ? " " : "") + units[number % 10]; + } + + if (number < 1000) { + return units[number / 100] + " hundred" + ((number % 100 != 0) ? " " : "") + convertToWord(number % 100); + } + + if (number < 1000000) { + return convertToWord(number / 1000) + " thousand" + ((number % 1000 != 0) ? " " : "") + convertToWord(number % 1000); + } + + if (number < 1000000000) { + return convertToWord(number / 1000000) + " million" + ((number % 1000000 != 0) ? " " : "") + convertToWord(number % 1000000); + } + + return convertToWord(number / 1000000000) + " billion" + ((number % 1000000000 != 0) ? " " : "") + convertToWord(number % 1000000000); + } +} diff --git a/src/main/java/com/ffii/core/utils/Params.java b/src/main/java/com/ffii/core/utils/Params.java new file mode 100644 index 0000000..5fa0b23 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/Params.java @@ -0,0 +1,42 @@ +package com.ffii.core.utils; + +/** @author Alex */ +public abstract class Params { + public static final String ERROR = "error"; + + public static final String SUCCESS = "success"; + public static final String DATA = "data"; + public static final String RECORDS = "records"; + public static final String TOTAL = "total"; + + public static final String ID = "id"; + public static final String CODE = "code"; + public static final String NAME = "name"; + public static final String TYPE = "type"; + public static final String MSG = "msg"; + public static final String MSG_CODE = "msgCode"; + public static final String MESSAGES = "messages"; + public static final String FROM = "from"; + public static final String TO = "to"; + + // sql + public static final String QUERY = "query"; + + // pagin + public static final String PAGE = "page"; + public static final String START = "start"; + public static final String LIMIT = "limit"; + + // filter + public static final String FILTER = "filter"; + public static final String OPERATOR = "operator"; + public static final String LIKE = "like"; + public static final String PROPERTY = "property"; + + // sort + public static final String SORT = "sort"; + public static final String DIRECTION = "direction"; + + public static final String VALUE = "value"; + +} diff --git a/src/main/java/com/ffii/core/utils/PasswordUtils.java b/src/main/java/com/ffii/core/utils/PasswordUtils.java new file mode 100644 index 0000000..be61b1e --- /dev/null +++ b/src/main/java/com/ffii/core/utils/PasswordUtils.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright 2Fi Business Solutions Ltd. + * + * This code is copyrighted. Under no circumstances should any party, people, + * or organization should redistribute any portions of this code in any form, + * either verbatim or through electronic media, to any third parties, unless + * under explicit written permission by 2Fi Business Solutions Ltd. + ******************************************************************************/ +package com.ffii.core.utils; + +import java.util.regex.Pattern; + +public abstract class PasswordUtils { + + private static final Pattern PATTERN_DIGITS = Pattern.compile("[0-9]"); + private static final Pattern PATTERN_A2Z_LOWER = Pattern.compile("[a-z]"); + private static final Pattern PATTERN_A2Z_UPPER = Pattern.compile("[A-Z]"); + // private static final String A2Z_LOWER = "abcdefghijklmnopqrstuvwxyz"; + // private static final String A2Z_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + // private static final String DIGITS = "0123456789"; + // private static final String SPECIAL_CHARS = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; + private static Pattern PATTERN_SPECIAL_CHARS = Pattern.compile("[!\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~]"); + + /* + * Ref: https://www.owasp.org/index.php/Password_special_characters + * without space character + */ + + public static final boolean checkPwd(String pwd, IPasswordRule rule, String username) { + if (pwd == null) return false; + if (pwd.length() < rule.getMin()) return false; + + if (rule.needSpecialCharacter() && !containsSpecialCharacters(pwd)) + return false; + if (rule.needNumberAndAlphabetic() && !containsAlphabeticAndNumericCharacters(pwd)) + return false; + if (rule.needNotContainUsername() && containsReferenceSubstring(pwd, username)) + return false; + if (rule.needNotContainThreeConsecutiveCharacters() && containsThreeConsecutiveCharacters(pwd)) + return false; + + return true; + } + + private static boolean containsSpecialCharacters(String input) { + return PATTERN_SPECIAL_CHARS.matcher(input).find(); + } + + private static boolean containsAlphabeticAndNumericCharacters(String input) { + boolean containsAlphabetic = PATTERN_A2Z_LOWER.matcher(input).find() || PATTERN_A2Z_UPPER.matcher(input).find(); + boolean containsNumeric = PATTERN_DIGITS.matcher(input).find(); + return containsAlphabetic && containsNumeric; + } + + private static boolean containsReferenceSubstring(String input, String username) { + return input.contains(username); + } + + private static boolean containsThreeConsecutiveCharacters(String input) { + for (int i = 0; i < input.length() - 2; i++) { + char currentChar = input.charAt(i); + char nextChar = input.charAt(i + 1); + char nextNextChar = input.charAt(i + 2); + if (currentChar == nextChar && nextChar == nextNextChar) { + return true; + } + } + return false; + } + + /*public static String genPwd(IPasswordRule rule, String username) { + int length = rule.getMin(); + + StringBuilder password = new StringBuilder(length); + Random random = new Random(System.nanoTime()); + + List charCategories = new ArrayList<>(4); + if (rule.needSpecialCharacter()) charCategories.add(SPECIAL_CHARS); + if (rule.needNumberAndAlphabetic()){ + charCategories.add(A2Z_UPPER); + charCategories.add(A2Z_LOWER); + charCategories.add(DIGITS); + } + + + if (rule.needSpecialChar()) charCategories.add(SPECIAL_CHARS); + + for (int i = 0; i < length; i++) { + String charCategory = charCategories.get(i % charCategories.size()); + char randomChar = charCategory.charAt(random.nextInt(charCategory.length())); + if (password.length() > 0) + password.insert(random.nextInt(password.length()), randomChar); + else + password.append(randomChar); + } + + return password.toString(); + }*/ + + public static interface IPasswordRule { + public Integer getMin(); + + public boolean needNumberAndAlphabetic(); + + public boolean needSpecialCharacter(); + + public boolean needNotContainUsername(); + + public boolean needNotContainThreeConsecutiveCharacters(); + } +} diff --git a/src/main/java/com/ffii/core/utils/RomanConverter.java b/src/main/java/com/ffii/core/utils/RomanConverter.java new file mode 100644 index 0000000..2eab689 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/RomanConverter.java @@ -0,0 +1,24 @@ +package com.ffii.core.utils; + +public class RomanConverter { + private static final int[] VALUES = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; + private static final String[] CAP_SYMBOLS = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; + private static final String[] SYMBOLS = { "m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i" }; + + public static String convertToRoman(int number, boolean isCapitalLetter) { + if (number <= 0 || number > 3999) { + throw new IllegalArgumentException("Number out of range. Please provide a value between 1 and 3999."); + } + + StringBuilder roman = new StringBuilder(); + + for (int i = 0; i < VALUES.length; i++) { + while (number >= VALUES[i]) { + roman.append(isCapitalLetter ? CAP_SYMBOLS[i] : SYMBOLS[i]); + number -= VALUES[i]; + } + } + + return roman.toString(); + } +} diff --git a/src/main/java/com/ffii/core/utils/StringUtils.java b/src/main/java/com/ffii/core/utils/StringUtils.java new file mode 100644 index 0000000..c363b79 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/StringUtils.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright 2017 2Fi Business Solutions Ltd. + * + * This code is part of the Core project. + * + * This code is copyrighted. Under no circumstances should any party, people, + * or organization should redistribute any portions of this code in any form, + * either verbatim or through electronic media, to any third parties, unless + * under explicit written permission by 2Fi Business Solutions Ltd. + ******************************************************************************/ +package com.ffii.core.utils; + +/** + * String Utils based on Apache Commons StringUtils. + * + * @author Patrick + */ +public abstract class StringUtils extends org.apache.commons.lang3.StringUtils { + + /** + * The String {@code "0"}. + */ + public static final String ZERO = "0"; + + /** + * The String {@code "1"}. + */ + public static final String ONE = "1"; + + /** + * The String {@code "%"}. + */ + public static final String PERCENT = "%"; + + /** + * The String {@code ","}. + */ + public static final String COMMA = ","; + + /** + * The String {@code "\r\n"} for line break on Windows + */ + public static final String LINE_BREAK_WINDOWS = "\r\n"; + + /** + * The String {@code "\n"} for line break on Unix/Linux + */ + public static final String LINE_BREAK_LINUX = "\n"; + + public static final String[] A2Z_LOWWER = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", + "o", "p", "q", "r", "s", "t", "u", "v", + "w", "x", "y", "z" }; + public static final String[] A2Z_UPPER = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T", "U", "V", + "W", "X", "Y", "Z" }; + + public static final String concat(String segregator, final String... chars) { + if (segregator == null) + segregator = ""; + String rs = ""; + for (String c : chars) { + if (c == null) + continue; + else { + if (StringUtils.isBlank(rs)) { + rs = c; + } else { + rs += segregator + c; + } + } + } + return rs; + } + + public static final String removeLineBreak(String str) { + if (str == null) + return str; + return str.replace("\r\n", " ") + .replace("\n", " ") + .replace("\r", " ") + .trim(); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/FhsmsCApplication.java b/src/main/java/com/ffii/fhsmsc/FhsmsCApplication.java new file mode 100644 index 0000000..f467e0d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/FhsmsCApplication.java @@ -0,0 +1,34 @@ +package com.ffii.fhsmsc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FhsmsCApplication { + + public static void main(String[] args) { + SpringApplication.run(FhsmsCApplication.class, args); + } + +} +/* + package com.ffii.fhsmsc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication +public class FhsmsCApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(FhsmsCApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(FhsmsCApplication.class, args); + } +} + */ \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/ServletInitializer.java b/src/main/java/com/ffii/fhsmsc/ServletInitializer.java new file mode 100644 index 0000000..5369947 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/ServletInitializer.java @@ -0,0 +1,12 @@ +package com.ffii.fhsmsc; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(FhsmsCApplication.class); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/config/AppConfig.java b/src/main/java/com/ffii/fhsmsc/config/AppConfig.java new file mode 100644 index 0000000..28bde3b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/AppConfig.java @@ -0,0 +1,33 @@ +package com.ffii.fhsmsc.config; + +import javax.sql.DataSource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +import com.ffii.core.support.JdbcDao; + +/** @author Terence */ +@Configuration +@ComponentScan(basePackages = { "com.ffii.core.*","com.ffii.fhsmsc.*"}) +@EnableScheduling +@EnableAsync +public class AppConfig { + + @Bean + @ConfigurationProperties(prefix = "spring.datasource") + public DataSource dataSource() { + return DataSourceBuilder.create().build(); + } + + @Bean + public JdbcDao jdbcDao(DataSource dataSource) { + return new JdbcDao(dataSource); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/config/OpenApi30Config.java b/src/main/java/com/ffii/fhsmsc/config/OpenApi30Config.java new file mode 100644 index 0000000..cb83c52 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/OpenApi30Config.java @@ -0,0 +1,20 @@ +package com.ffii.fhsmsc.config; + +import org.springframework.context.annotation.Configuration; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@Configuration +@OpenAPIDefinition(info = @Info(title = "My API", version = "v1")) +@SecurityScheme( + name = "bearerAuth", + type = SecuritySchemeType.HTTP, + bearerFormat = "JWT", + scheme = "bearer" +) +public class OpenApi30Config { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/config/RestTemplateConfig.java b/src/main/java/com/ffii/fhsmsc/config/RestTemplateConfig.java new file mode 100644 index 0000000..4188a01 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/RestTemplateConfig.java @@ -0,0 +1,15 @@ +package com.ffii.fhsmsc.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/config/WebConfig.java b/src/main/java/com/ffii/fhsmsc/config/WebConfig.java new file mode 100644 index 0000000..cf5d80d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/WebConfig.java @@ -0,0 +1,33 @@ +package com.ffii.fhsmsc.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +@Configuration +@EnableWebMvc +public class WebConfig implements WebMvcConfigurer { + + // @Value("${host.url}") + // private String url; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedHeaders("*") + .allowedOrigins("*") + .exposedHeaders("filename") + .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"); + + } + + @Bean + public InternalResourceViewResolver defaultViewResolver() { + return new InternalResourceViewResolver(); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/config/security/SecurityConfig.java b/src/main/java/com/ffii/fhsmsc/config/security/SecurityConfig.java new file mode 100644 index 0000000..e19a214 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/security/SecurityConfig.java @@ -0,0 +1,101 @@ +package com.ffii.fhsmsc.config.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.ffii.fhsmsc.config.security.jwt.JwtRequestFilter; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +public class SecurityConfig { + + public static final String INDEX_URL = "/"; + public static final String LOGIN_URL = "/login"; + public static final String REFRESH_TOKEN_URL = "/refresh-token"; + public static final String FOOD_ITEMS_LIST_URL = "/foodItems/save"; + public static final String FOOD_ITEMS_SAVE_URL = "/foodItems/**"; + public static final String check_token_URL = "/user_information/validate_token/**"; + public static final String FOOD1_URL="/food1/**"; + public static final String REGISTRY_URL = "/user/registry"; + public static final String SWAGGER_URL = "/v3/api-docs/**"; + public static final String SWAGGER_INDEX_URL = "/swagger-ui/**"; + public static final String RESTAURANT_INFORMATION_URL = "/FB_Operator/**"; + public static final String RESTAURANT_OUTLETS_URL = "/FB_outlets/**"; + public static final String FB_MENU_ITEM_NUTRITION_URL = "/fb_menu_item_nutrition/**"; + public static final String FB_MENU_ITEM_INGREDIENT_URL = "/fb_menu_item_ingredient/**"; + public static final String FB_MENU_AND_OUTLET_RELATIONSHIP_URL = "/fb_menu_and_outlet_relationship/**"; + public static final String FB_MENU_URL = "/fb_menu/**"; + public static final String FB_MENU_ITEM_BASE_EMBEDDING_URL = "/embedding/**"; + public static final String MENU_URL = "/menu/**"; + public static final String COMBINED_URL = "/combined/**"; + public static final String DAILYNUTRITTIONSUMARY_URL = "/DailyNutritionSummary/**"; + public static final String[] URL_WHITELIST = { + INDEX_URL, + LOGIN_URL, + REFRESH_TOKEN_URL, + FOOD_ITEMS_LIST_URL, + FOOD_ITEMS_SAVE_URL, + FOOD1_URL, + REGISTRY_URL, + SWAGGER_URL, + SWAGGER_INDEX_URL, + check_token_URL, + RESTAURANT_INFORMATION_URL, + RESTAURANT_OUTLETS_URL, + FB_MENU_ITEM_NUTRITION_URL, + FB_MENU_ITEM_INGREDIENT_URL, + FB_MENU_AND_OUTLET_RELATIONSHIP_URL, + FB_MENU_URL, + MENU_URL, + DAILYNUTRITTIONSUMARY_URL, + FB_MENU_ITEM_BASE_EMBEDDING_URL, + COMBINED_URL + }; + + @Lazy + @Autowired + private JwtRequestFilter jwtRequestFilter; + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) + throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + @Order(1) + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .cors(Customizer.withDefaults()).csrf(csrf -> csrf.disable()) + .requestCache(requestCache -> requestCache.disable()) + .authorizeHttpRequests( + authRequest -> authRequest.requestMatchers(URL_WHITELIST).permitAll().anyRequest().authenticated()) + .httpBasic(httpBasic -> httpBasic.authenticationEntryPoint( + (request, response, authException) -> response.sendError(HttpStatus.UNAUTHORIZED.value()))) + .sessionManagement( + sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/config/security/jwt/JwtRequestFilter.java b/src/main/java/com/ffii/fhsmsc/config/security/jwt/JwtRequestFilter.java new file mode 100644 index 0000000..41a927b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/security/jwt/JwtRequestFilter.java @@ -0,0 +1,78 @@ +package com.ffii.fhsmsc.config.security.jwt; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.ffii.core.utils.JwtTokenUtil; +import com.ffii.fhsmsc.config.security.jwt.service.JwtUserDetailsService; + +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.security.SignatureException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class JwtRequestFilter extends OncePerRequestFilter { + + @Autowired + private JwtUserDetailsService jwtUserDetailsService; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + + final String requestTokenHeader = request.getHeader("Authorization"); + + String username = null; + String jwtToken = null; + // JWT Token is in the form "Bearer token". Remove Bearer word and get + // only the Token + if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { + jwtToken = requestTokenHeader.substring(7).replaceAll("\"", ""); + try { + username = jwtTokenUtil.getUsernameFromToken(jwtToken); + } catch (IllegalArgumentException e) { + logger.error("Unable to get JWT Token"); + } catch (ExpiredJwtException e) { + logger.error("JWT Token has expired"); + } catch (SignatureException e) { + logger.error("JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trust."); + } + } else { + logger.warn("JWT Token does not begin with Bearer String"); + } + + // Once we get the token validate it. + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + + UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username); + + // if token is valid configure Spring Security to manually set + // authentication + if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { + + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + // After setting the Authentication in the context, we specify + // that the current user is authenticated. So it passes the + // Spring Security Configurations successfully. + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + } + } + chain.doFilter(request, response); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/config/security/jwt/service/JwtUserDetailsService.java b/src/main/java/com/ffii/fhsmsc/config/security/jwt/service/JwtUserDetailsService.java new file mode 100644 index 0000000..7d1f0c9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/security/jwt/service/JwtUserDetailsService.java @@ -0,0 +1,31 @@ +package com.ffii.fhsmsc.config.security.jwt.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import com.ffii.fhsmsc.modules.user.entity.User; +import com.ffii.fhsmsc.modules.user.entity.UserRepository; +import com.ffii.fhsmsc.modules.user.service.UserAuthorityService; +import com.ffii.fhsmsc.modules.user.service.UserService; + +@Service +public class JwtUserDetailsService implements UserDetailsService { + + @Autowired + UserRepository userRepository; + + @Autowired + UserAuthorityService userAuthService; + + @Autowired + UserService userService; + + + @Override + public User loadUserByUsername(String username) throws UsernameNotFoundException { + return userService.loadUserOptByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username)); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/config/security/jwt/web/JwtAuthenticationController.java b/src/main/java/com/ffii/fhsmsc/config/security/jwt/web/JwtAuthenticationController.java new file mode 100644 index 0000000..6cc67c0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/security/jwt/web/JwtAuthenticationController.java @@ -0,0 +1,195 @@ +package com.ffii.fhsmsc.config.security.jwt.web; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import com.ffii.core.response.AuthRes; +import com.ffii.core.utils.AES; +import com.ffii.core.utils.JwtTokenUtil; +import com.ffii.fhsmsc.config.security.jwt.service.JwtUserDetailsService; +import com.ffii.fhsmsc.config.security.service.LoginLogService; +import com.ffii.fhsmsc.model.ExceptionResponse; +import com.ffii.fhsmsc.model.JwtRequest; +import com.ffii.fhsmsc.model.JwtResponse; +import com.ffii.fhsmsc.model.RefreshToken; +import com.ffii.fhsmsc.model.TokenRefreshRequest; +import com.ffii.fhsmsc.model.TokenRefreshResponse; +import com.ffii.fhsmsc.modules.common.SettingNames; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; +import com.ffii.fhsmsc.modules.user.entity.User; +import com.ffii.fhsmsc.modules.user.entity.UserRepository; +import com.ffii.fhsmsc.modules.user.service.UserAuthorityService; +import org.springframework.web.bind.annotation.RequestMapping; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +public class JwtAuthenticationController { + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + @Autowired + private JwtUserDetailsService userDetailsService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserAuthorityService userAuthorityService; + + @Autowired + private LoginLogService loginLogService; + + @Autowired + private SettingsService settingsService; + + private static final long EXPIRY_IN_MINTUE = 60000; + private static final long TOKEN_DURATION = 30; + + @PostMapping("/login") + public ResponseEntity login(@RequestBody JwtRequest authenticationRequest, HttpServletRequest request) + throws Exception { + String username = authenticationRequest.getUsername(); + HttpStatus httpStatus = HttpStatus.UNAUTHORIZED; + String message = "Invalid username and password."; + Integer maxAttempt = settingsService.getInt(SettingNames.SYS_LOGIN_ATTEMPT_LIMIT); + Integer penalityTime = settingsService.getInt(SettingNames.SYS_LOGIN_ATTEMPT_PENALITY_TIME); + String checkAddrr = request.getHeader("X-Forwarded-For"); + if (checkAddrr == null || "".equals(checkAddrr)) { + checkAddrr = request.getRemoteAddr(); + } + List> failAttempts = loginLogService.failChecking(username, checkAddrr, maxAttempt); + if (failAttempts.size() >= maxAttempt) { + // check if last failAttempts not contain sucess login then trigger penality + // time + if (!failAttempts.stream().map(x -> { + return x.get("success"); + }).collect(Collectors.toList()).contains(true)) { + LocalDateTime lastAttemptTime = (LocalDateTime) failAttempts.get(0).get("loginTime"); + if (LocalDateTime.now().isBefore(lastAttemptTime.plusMinutes(penalityTime))) { + return ResponseEntity.status(httpStatus) + .body(new ExceptionResponse( + "Login failure reach the limit.\n Please try again after " + // add 1 to round up + + (ChronoUnit.MINUTES.between(LocalDateTime.now(), lastAttemptTime.plusMinutes(penalityTime)) + 1) + + " minutes.", + null)); + } + } + } + + AuthRes authRes = authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); + + UserDetails userDetails = null; + if (authRes.isSuccess()) { + try { + userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); + } catch (UsernameNotFoundException e) { + message = username + " not yet register in the system."; + authRes.setSuccess(false); + authRes.setException(ExceptionUtils.getStackTrace(e)); + } catch (Exception e) { + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + message = "Internal Server Error"; + authRes.setSuccess(false); + authRes.setException(ExceptionUtils.getStackTrace(e)); + } + } + + String remoteAddrr = request.getHeader("X-Forwarded-For"); + if (remoteAddrr == null || "".equals(remoteAddrr)) { + remoteAddrr = request.getRemoteAddr(); + } + + loginLogService.createLoginLog(username, remoteAddrr, authRes.isSuccess()); + + if (userDetails == null) { + return ResponseEntity.status(httpStatus) + .body(new ExceptionResponse(message, authRes.getException())); + } else { + User user = userRepository.findByName(userDetails.getUsername()).get(0); + if (user.isLocked()) { + return ResponseEntity.status(httpStatus) + .body(new ExceptionResponse(username + " has been locked.", null)); + } + } + + return createAuthTokenResponse(userDetails); + } + + private AuthRes authenticate(String username, String password) throws Exception { + boolean success = false; + String exception = ""; + try { + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); + success = true; + } catch (Exception e) { + exception = ExceptionUtils.getStackTrace(e); + } + return new AuthRes(success, exception); + } + + private ResponseEntity createAuthTokenResponse(UserDetails userDetails) { + long accessTokenExpiry = /* settingsService.getInt(SettingNames.SYS_IDLE_LOGOUT_TIME) */ TOKEN_DURATION + * EXPIRY_IN_MINTUE; + final String accessToken = jwtTokenUtil.generateToken(userDetails, accessTokenExpiry); + final String refreshToken = jwtTokenUtil.createRefreshToken(userDetails.getUsername()).getToken(); + + User user = userRepository.findByUsernameAndDeletedFalse(userDetails.getUsername()).get(); + + List abilities = new ArrayList(); + userAuthorityService.getUserAuthority(user).forEach(auth -> abilities.add(auth.get("authority").toString())); + + return ResponseEntity.ok(new JwtResponse(accessToken, refreshToken, null, user, abilities)); + } + + @PostMapping("/refresh-token") + public ResponseEntity refreshtoken(@Valid @RequestBody TokenRefreshRequest request) + throws Exception { + long accessTokenExpiry = /* settingsService.getInt(SettingNames.SYS_IDLE_LOGOUT_TIME) */ TOKEN_DURATION + * EXPIRY_IN_MINTUE; + String requestRefreshToken = request.getRefreshToken(); + + requestRefreshToken = requestRefreshToken.replaceAll("\"", ""); + String[] decryptStringList = AES.decrypt(requestRefreshToken, JwtTokenUtil.AES_SECRET) + .split(JwtTokenUtil.TOKEN_SEPARATOR); + RefreshToken instance = new RefreshToken(); + String username = decryptStringList[0]; + instance.setExpiryDate(Instant.ofEpochMilli(Long.valueOf(decryptStringList[1]))); + instance.setToken(requestRefreshToken); + instance.setUserName(decryptStringList[0]); + + if (!jwtTokenUtil.verifyExpiration(instance)) { + throw new ResponseStatusException(HttpStatus.EXPECTATION_FAILED, + "Refresh token was expired. Please make a new signin request"); + } + + final UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + String accessToken = jwtTokenUtil.generateToken(userDetails, accessTokenExpiry); + String refreshToken = jwtTokenUtil.createRefreshToken(username).getToken(); + return ResponseEntity.ok(new TokenRefreshResponse(accessToken, refreshToken)); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/config/security/service/LoginLogService.java b/src/main/java/com/ffii/fhsmsc/config/security/service/LoginLogService.java new file mode 100644 index 0000000..08d9ec2 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/config/security/service/LoginLogService.java @@ -0,0 +1,54 @@ +package com.ffii.fhsmsc.config.security.service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.support.AbstractService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.MapUtils; + +@Service +public class LoginLogService extends AbstractService { + + public LoginLogService(JdbcDao jdbcDao) { + super(jdbcDao); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = false) + public boolean createLoginLog(String username, String remoteAddr, boolean success) { + String sql = "INSERT INTO user_login_log (`username`, `loginTime`, `ipAddr`, `success`) " + +"VALUES (:username, :loginTime, :ipAddr, :success)"; + Map args = new HashMap<>(4); + args.put("username", username); + args.put("loginTime", new Date()); + args.put("ipAddr", remoteAddr); + args.put("success", success); + + return (jdbcDao.executeUpdate(sql, args) == 1); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public List> listLastLog(String username, int limit) { + return jdbcDao.queryForList("SELECT success FROM user_login_log where username = :username ORDER BY loginTime DESC LIMIT " + limit, + MapUtils.toHashMap("username", username)); + } + + public List> failChecking(String username,String remoteAddrr, Integer maxAttempt){ + StringBuilder sql = new StringBuilder("SELECT" + + " * " + + " FROM user_login_log ull " + + " WHERE ull.ipAddr = :remoteAddrr " + + " ORDER BY ull.loginTime DESC " + + " LIMIT :maxAttempt " + ); + + return jdbcDao.queryForList(sql.toString(), Map.of("username", username, "remoteAddrr", remoteAddrr, "maxAttempt", maxAttempt)); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/model/AbilityModel.java b/src/main/java/com/ffii/fhsmsc/model/AbilityModel.java new file mode 100644 index 0000000..31d3bfd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/AbilityModel.java @@ -0,0 +1,13 @@ +package com.ffii.fhsmsc.model; + +public class AbilityModel { + + private final String actionSubjectCombo; + public AbilityModel(String actionSubjectCombo) { + this.actionSubjectCombo = actionSubjectCombo; + } + + public String getActionSubjectCombo() { + return actionSubjectCombo; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/model/ExceptionResponse.java b/src/main/java/com/ffii/fhsmsc/model/ExceptionResponse.java new file mode 100644 index 0000000..335b356 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/ExceptionResponse.java @@ -0,0 +1,28 @@ +package com.ffii.fhsmsc.model; + +public class ExceptionResponse { + private String message; + private String exception; + + public ExceptionResponse(String message, String exception) { + this.message = message; + this.exception = exception; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getException() { + return exception; + } + + public void setException(String exception) { + this.exception = exception; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/model/JwtRequest.java b/src/main/java/com/ffii/fhsmsc/model/JwtRequest.java new file mode 100644 index 0000000..ff39359 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/JwtRequest.java @@ -0,0 +1,45 @@ +package com.ffii.fhsmsc.model; + + +import java.io.Serializable; + +import jakarta.validation.constraints.NotNull; + +public class JwtRequest implements Serializable { + + private static final long serialVersionUID = 5926468583005150707L; + + @NotNull + private String username; + + @NotNull + private String password; + + //need default constructor for JSON Parsing + public JwtRequest() + { + + } + + public JwtRequest(String username, String password) { + this.setUsername(username); + this.setPassword(password); + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/model/JwtResponse.java b/src/main/java/com/ffii/fhsmsc/model/JwtResponse.java new file mode 100644 index 0000000..78e94d1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/JwtResponse.java @@ -0,0 +1,60 @@ +package com.ffii.fhsmsc.model; + +import java.io.Serializable; +import java.util.List; + +import com.ffii.fhsmsc.modules.user.entity.User; + +public class JwtResponse implements Serializable { + + private static final long serialVersionUID = -8091879091924046844L; + private final Long id; + private final String name; + private final String email; + private final String accessToken; + private final String refreshToken; + private final String role; + private final List abilities; + private final boolean has_information; + public JwtResponse(String accessToken, String refreshToken, String role, User user, /*Set*/List abilities) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.role = role; + this.id = user.getId(); + this.name = user.getName(); + this.email = user.getEmail(); + this.has_information=user.getHas_information(); + this.abilities = abilities; + } + + public String getAccessToken() { + return this.accessToken; + } + public boolean gethas_information() { + return this.has_information; + } + public String getRole() { + return role; + } + + public String getRefreshToken() { + return refreshToken; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + public List/*Set*/ getAbilities() { + return abilities; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/model/RefreshToken.java b/src/main/java/com/ffii/fhsmsc/model/RefreshToken.java new file mode 100644 index 0000000..1061689 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/RefreshToken.java @@ -0,0 +1,39 @@ +package com.ffii.fhsmsc.model; + +import java.time.Instant; + +public class RefreshToken { + + private String userName; + + private String token; + + private Instant expiryDate; + + + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public Instant getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(Instant expiryDate) { + this.expiryDate = expiryDate; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/model/TokenRefreshRequest.java b/src/main/java/com/ffii/fhsmsc/model/TokenRefreshRequest.java new file mode 100644 index 0000000..5f4bdb2 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/TokenRefreshRequest.java @@ -0,0 +1,16 @@ +package com.ffii.fhsmsc.model; +import jakarta.validation.constraints.NotBlank; + +public class TokenRefreshRequest { + @NotBlank + private String refreshToken; + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/model/TokenRefreshResponse.java b/src/main/java/com/ffii/fhsmsc/model/TokenRefreshResponse.java new file mode 100644 index 0000000..8e8d497 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/model/TokenRefreshResponse.java @@ -0,0 +1,37 @@ +package com.ffii.fhsmsc.model; + +public class TokenRefreshResponse { + private String accessToken; + private String refreshToken; + private String tokenType = "Bearer"; + + public TokenRefreshResponse(String accessToken, String refreshToken) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Combine/res/FBCombinedListRes.java b/src/main/java/com/ffii/fhsmsc/modules/Combine/res/FBCombinedListRes.java new file mode 100644 index 0000000..bf51cec --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Combine/res/FBCombinedListRes.java @@ -0,0 +1,86 @@ +package com.ffii.fhsmsc.modules.Combine.res; + +import java.util.List; + +public class FBCombinedListRes { + private OperatorInfo operator; + private List brands; + + public static class OperatorInfo { + private Long id; + private String FB_name; + private String FB_operator_address; + private String FB_operator_phone; + private String user_Grp_Org_ID; + + // Getters and Setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getFB_name() { return FB_name; } + public void setFB_name(String FB_name) { this.FB_name = FB_name; } + public String getFB_operator_address() { return FB_operator_address; } + public void setFB_operator_address(String FB_operator_address) { this.FB_operator_address = FB_operator_address; } + public String getFB_operator_phone() { return FB_operator_phone; } + public void setFB_operator_phone(String FB_operator_phone) { this.FB_operator_phone = FB_operator_phone; } + public String getUser_Grp_Org_ID() { return user_Grp_Org_ID; } + public void setUser_Grp_Org_ID(String user_Grp_Org_ID) { this.user_Grp_Org_ID = user_Grp_Org_ID; } + } + + public static class BrandInfo { + private Long fb_id; + private String fb_name_en; + private String fb_name_zh; + private String fb_type_en; + private String fb_type_zh; + private String fb_cuisine_en; + private String fb_cuisine_zh; + private List outlets; + + // Getters and Setters + public Long getFb_id() { return fb_id; } + public void setFb_id(Long fb_id) { this.fb_id = fb_id; } + public String getFb_name_en() { return fb_name_en; } + public void setFb_name_en(String fb_name_en) { this.fb_name_en = fb_name_en; } + public String getFb_name_zh() { return fb_name_zh; } + public void setFb_name_zh(String fb_name_zh) { this.fb_name_zh = fb_name_zh; } + public String getFb_type_en() { return fb_type_en; } + public void setFb_type_en(String fb_type_en) { this.fb_type_en = fb_type_en; } + public String getFb_type_zh() { return fb_type_zh; } + public void setFb_type_zh(String fb_type_zh) { this.fb_type_zh = fb_type_zh; } + public String getFb_cuisine_en() { return fb_cuisine_en; } + public void setFb_cuisine_en(String fb_cuisine_en) { this.fb_cuisine_en = fb_cuisine_en; } + public String getFb_cuisine_zh() { return fb_cuisine_zh; } + public void setFb_cuisine_zh(String fb_cuisine_zh) { this.fb_cuisine_zh = fb_cuisine_zh; } + public List getOutlets() { return outlets; } + public void setOutlets(List outlets) { this.outlets = outlets; } + } + + public static class OutletInfo { + private Long id; + private String fb_outlet_district_en; + private String fb_outlet_district_zh; + private String fb_outlet_address_en; + private String fb_outlet_address_zh; + private String fb_outlet_phone; + + // Getters and Setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getFb_outlet_district_en() { return fb_outlet_district_en; } + public void setFb_outlet_district_en(String fb_outlet_district_en) { this.fb_outlet_district_en = fb_outlet_district_en; } + public String getFb_outlet_district_zh() { return fb_outlet_district_zh; } + public void setFb_outlet_district_zh(String fb_outlet_district_zh) { this.fb_outlet_district_zh = fb_outlet_district_zh; } + public String getFb_outlet_address_en() { return fb_outlet_address_en; } + public void setFb_outlet_address_en(String fb_outlet_address_en) { this.fb_outlet_address_en = fb_outlet_address_en; } + public String getFb_outlet_address_zh() { return fb_outlet_address_zh; } + public void setFb_outlet_address_zh(String fb_outlet_address_zh) { this.fb_outlet_address_zh = fb_outlet_address_zh; } + public String getFb_outlet_phone() { return fb_outlet_phone; } + public void setFb_outlet_phone(String fb_outlet_phone) { this.fb_outlet_phone = fb_outlet_phone; } + } + + // Getters and Setters + public OperatorInfo getOperator() { return operator; } + public void setOperator(OperatorInfo operator) { this.operator = operator; } + public List getBrands() { return brands; } + public void setBrands(List brands) { this.brands = brands; } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummary.java b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummary.java new file mode 100644 index 0000000..627736f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummary.java @@ -0,0 +1,277 @@ +package com.ffii.fhsmsc.modules.DailyNutritionSummary.enity; +import java.math.BigDecimal; +import java.util.Date; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "daily_nutrition_summary") +public class DailyNutritionSummary extends BaseEntity { + + @Column + private Long id; + @Column + private Long user_id; + @Column + private Long User_Grp_Org_ID; + @Column + private Date date; + @Column + private String visibility_level; + @Column + private String data_category; + @Column + private Long reference_group_id; + @Column + private Long health_metrics_reference_id; + @Column + private BigDecimal total_calories; + @Column + private BigDecimal total_protein; + @Column + private BigDecimal total_fat; + @Column + private BigDecimal total_carbs; + @Column + private BigDecimal total_sodium; + + @Column + private BigDecimal total_potassium; + @Column + private BigDecimal total_calcium; + @Column + private BigDecimal total_phosphorus; + @Column + private BigDecimal total_iron; + @Column + private BigDecimal total_vitamin_a; + @Column + private BigDecimal total_vitamin_b1; + @Column + private BigDecimal total_vitamin_b2; + @Column + private BigDecimal total_niacin; + @Column + private BigDecimal total_vitamin_c; + @Column + private BigDecimal breakfast_calories; + @Column + private BigDecimal lunch_calories; + @Column + private BigDecimal dinner_calories; + @Column + private BigDecimal snack_calories; + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getVisibility_level() { + return visibility_level; + } + + public void setVisibility_level(String visibility_level) { + this.visibility_level = visibility_level; + } + + public String getData_category() { + return data_category; + } + + public void setData_category(String data_category) { + this.data_category = data_category; + } + + public Long getReference_group_id() { + return reference_group_id; + } + + public void setReference_group_id(Long reference_group_id) { + this.reference_group_id = reference_group_id; + } + + public Long getHealth_metrics_reference_id() { + return health_metrics_reference_id; + } + + public void setHealth_metrics_reference_id(Long health_metrics_reference_id) { + this.health_metrics_reference_id = health_metrics_reference_id; + } + + public BigDecimal getTotal_calories() { + return total_calories; + } + + public void setTotal_calories(BigDecimal total_calories) { + this.total_calories = total_calories; + } + + public BigDecimal getTotal_protein() { + return total_protein; + } + + public void setTotal_protein(BigDecimal total_protein) { + this.total_protein = total_protein; + } + + public BigDecimal getTotal_fat() { + return total_fat; + } + + public void setTotal_fat(BigDecimal total_fat) { + this.total_fat = total_fat; + } + + public BigDecimal getTotal_carbs() { + return total_carbs; + } + + public void setTotal_carbs(BigDecimal total_carbs) { + this.total_carbs = total_carbs; + } + + public BigDecimal getTotal_sodium() { + return total_sodium; + } + + public void setTotal_sodium(BigDecimal total_sodium) { + this.total_sodium = total_sodium; + } + + public BigDecimal getTotal_potassium() { + return total_potassium; + } + + public void setTotal_potassium(BigDecimal total_potassium) { + this.total_potassium = total_potassium; + } + + public BigDecimal getTotal_calcium() { + return total_calcium; + } + + public void setTotal_calcium(BigDecimal total_calcium) { + this.total_calcium = total_calcium; + } + + public BigDecimal getTotal_phosphorus() { + return total_phosphorus; + } + + public void setTotal_phosphorus(BigDecimal total_phosphorus) { + this.total_phosphorus = total_phosphorus; + } + + public BigDecimal getTotal_iron() { + return total_iron; + } + + public void setTotal_iron(BigDecimal total_iron) { + this.total_iron = total_iron; + } + + public BigDecimal getTotal_vitamin_a() { + return total_vitamin_a; + } + + public void setTotal_vitamin_a(BigDecimal total_vitamin_a) { + this.total_vitamin_a = total_vitamin_a; + } + + public BigDecimal getTotal_vitamin_b1() { + return total_vitamin_b1; + } + + public void setTotal_vitamin_b1(BigDecimal total_vitamin_b1) { + this.total_vitamin_b1 = total_vitamin_b1; + } + + public BigDecimal getTotal_vitamin_b2() { + return total_vitamin_b2; + } + + public void setTotal_vitamin_b2(BigDecimal total_vitamin_b2) { + this.total_vitamin_b2 = total_vitamin_b2; + } + + public BigDecimal getTotal_niacin() { + return total_niacin; + } + + public void setTotal_niacin(BigDecimal total_niacin) { + this.total_niacin = total_niacin; + } + + public BigDecimal getTotal_vitamin_c() { + return total_vitamin_c; + } + + public void setTotal_vitamin_c(BigDecimal total_vitamin_c) { + this.total_vitamin_c = total_vitamin_c; + } + + public BigDecimal getBreakfast_calories() { + return breakfast_calories; + } + + public void setBreakfast_calories(BigDecimal breakfast_calories) { + this.breakfast_calories = breakfast_calories; + } + + public BigDecimal getLunch_calories() { + return lunch_calories; + } + + public void setLunch_calories(BigDecimal lunch_calories) { + this.lunch_calories = lunch_calories; + } + + public BigDecimal getDinner_calories() { + return dinner_calories; + } + + public void setDinner_calories(BigDecimal dinner_calories) { + this.dinner_calories = dinner_calories; + } + + public BigDecimal getSnack_calories() { + return snack_calories; + } + + public void setSnack_calories(BigDecimal snack_calories) { + this.snack_calories = snack_calories; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummaryRepository.java b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummaryRepository.java new file mode 100644 index 0000000..e372932 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/enity/DailyNutritionSummaryRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.DailyNutritionSummary.enity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DailyNutritionSummaryRepository extends JpaRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/req/DailyNutritionSummaryReq.java b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/req/DailyNutritionSummaryReq.java new file mode 100644 index 0000000..19fdce3 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/req/DailyNutritionSummaryReq.java @@ -0,0 +1,329 @@ +package com.ffii.fhsmsc.modules.DailyNutritionSummary.req; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + + + +public class DailyNutritionSummaryReq { + + private Long id; + + private Long user_id; + + private Long User_Grp_Org_ID; + + private Date date; + + private String visibility_level; + + private String data_category; + + private Long reference_group_id; + + private Long health_metrics_reference_id; + + private BigDecimal total_calories; + + private BigDecimal total_protein; + + private BigDecimal total_fat; + + private BigDecimal total_carbs; + + private BigDecimal total_sodium; + + + private BigDecimal total_potassium; + + private BigDecimal total_calcium; + + private BigDecimal total_phosphorus; + + private BigDecimal total_iron; + + private BigDecimal total_vitamin_a; + + private BigDecimal total_vitamin_b1; + + private BigDecimal total_vitamin_b2; + + private BigDecimal total_niacin; + + private BigDecimal total_vitamin_c; + + private BigDecimal breakfast_calories; + + private BigDecimal lunch_calories; + + private BigDecimal dinner_calories; + + private BigDecimal snack_calories; + + private LocalDateTime created; + + private Long createdBy; + + private LocalDateTime modified; + + private Long modifiedBy; + + private Integer version; + + private Boolean deleted; + + + public Long getId() { + return id; + } + + + public void setId(Long id) { + this.id = id; + } + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getVisibility_level() { + return visibility_level; + } + + public void setVisibility_level(String visibility_level) { + this.visibility_level = visibility_level; + } + + public String getData_category() { + return data_category; + } + + public void setData_category(String data_category) { + this.data_category = data_category; + } + + public Long getReference_group_id() { + return reference_group_id; + } + + public void setReference_group_id(Long reference_group_id) { + this.reference_group_id = reference_group_id; + } + + public Long getHealth_metrics_reference_id() { + return health_metrics_reference_id; + } + + public void setHealth_metrics_reference_id(Long health_metrics_reference_id) { + this.health_metrics_reference_id = health_metrics_reference_id; + } + + public BigDecimal getTotal_calories() { + return total_calories; + } + + public void setTotal_calories(BigDecimal total_calories) { + this.total_calories = total_calories; + } + + public BigDecimal getTotal_protein() { + return total_protein; + } + + public void setTotal_protein(BigDecimal total_protein) { + this.total_protein = total_protein; + } + + public BigDecimal getTotal_fat() { + return total_fat; + } + + public void setTotal_fat(BigDecimal total_fat) { + this.total_fat = total_fat; + } + + public BigDecimal getTotal_carbs() { + return total_carbs; + } + + public void setTotal_carbs(BigDecimal total_carbs) { + this.total_carbs = total_carbs; + } + + public BigDecimal getTotal_sodium() { + return total_sodium; + } + + public void setTotal_sodium(BigDecimal total_sodium) { + this.total_sodium = total_sodium; + } + + public BigDecimal getTotal_potassium() { + return total_potassium; + } + + public void setTotal_potassium(BigDecimal total_potassium) { + this.total_potassium = total_potassium; + } + + public BigDecimal getTotal_calcium() { + return total_calcium; + } + + public void setTotal_calcium(BigDecimal total_calcium) { + this.total_calcium = total_calcium; + } + + public BigDecimal getTotal_phosphorus() { + return total_phosphorus; + } + + public void setTotal_phosphorus(BigDecimal total_phosphorus) { + this.total_phosphorus = total_phosphorus; + } + + public BigDecimal getTotal_iron() { + return total_iron; + } + + public void setTotal_iron(BigDecimal total_iron) { + this.total_iron = total_iron; + } + + public BigDecimal getTotal_vitamin_a() { + return total_vitamin_a; + } + + public void setTotal_vitamin_a(BigDecimal total_vitamin_a) { + this.total_vitamin_a = total_vitamin_a; + } + + public BigDecimal getTotal_vitamin_b1() { + return total_vitamin_b1; + } + + public void setTotal_vitamin_b1(BigDecimal total_vitamin_b1) { + this.total_vitamin_b1 = total_vitamin_b1; + } + + public BigDecimal getTotal_vitamin_b2() { + return total_vitamin_b2; + } + + public void setTotal_vitamin_b2(BigDecimal total_vitamin_b2) { + this.total_vitamin_b2 = total_vitamin_b2; + } + + public BigDecimal getTotal_niacin() { + return total_niacin; + } + + public void setTotal_niacin(BigDecimal total_niacin) { + this.total_niacin = total_niacin; + } + + public BigDecimal getTotal_vitamin_c() { + return total_vitamin_c; + } + + public void setTotal_vitamin_c(BigDecimal total_vitamin_c) { + this.total_vitamin_c = total_vitamin_c; + } + + public BigDecimal getBreakfast_calories() { + return breakfast_calories; + } + + public void setBreakfast_calories(BigDecimal breakfast_calories) { + this.breakfast_calories = breakfast_calories; + } + + public BigDecimal getLunch_calories() { + return lunch_calories; + } + + public void setLunch_calories(BigDecimal lunch_calories) { + this.lunch_calories = lunch_calories; + } + + public BigDecimal getDinner_calories() { + return dinner_calories; + } + + public void setDinner_calories(BigDecimal dinner_calories) { + this.dinner_calories = dinner_calories; + } + + public BigDecimal getSnack_calories() { + return snack_calories; + } + + public void setSnack_calories(BigDecimal snack_calories) { + this.snack_calories = snack_calories; + } + + public LocalDateTime getCreated() { + return created; + } + + public void setCreated(LocalDateTime created) { + this.created = created; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getModified() { + return modified; + } + + public void setModified(LocalDateTime modified) { + this.modified = modified; + } + + public Long getModifiedBy() { + return modifiedBy; + } + + public void setModifiedBy(Long modifiedBy) { + this.modifiedBy = modifiedBy; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/service/DailyNutritionSummaryService.java b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/service/DailyNutritionSummaryService.java new file mode 100644 index 0000000..9a3180d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/service/DailyNutritionSummaryService.java @@ -0,0 +1,150 @@ +package com.ffii.fhsmsc.modules.DailyNutritionSummary.service; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.enity.DailyNutritionSummary; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.enity.DailyNutritionSummaryRepository; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.req.DailyNutritionSummaryReq; + +@Service +public class DailyNutritionSummaryService { + private final JdbcDao jdbcDao; + private final DailyNutritionSummaryRepository repository; + + public DailyNutritionSummaryService(JdbcDao jdbcDao, DailyNutritionSummaryRepository repository) { + this.jdbcDao = jdbcDao; + this.repository = repository; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM daily_nutrition_summary fi" + + " WHERE 1=1 AND fi.deleted = 0"); + + if (args != null) { + if (args.containsKey("user_id")) sql.append(" AND fi.user_id = :user_id"); + if (args.containsKey("date")) sql.append(" AND DATE(fi.date) = DATE(:date)"); + if (args.containsKey("start_date")) sql.append(" AND DATE(fi.date) >= DATE(:start_date)"); + if (args.containsKey("end_date")) sql.append(" AND DATE(fi.date) <= DATE(:end_date)"); + } + + sql.append(" ORDER BY fi.date DESC, fi.id DESC"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public DailyNutritionSummary saveOrUpdate(DailyNutritionSummaryReq req) { + DailyNutritionSummary instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new DailyNutritionSummary(); + isNew = true; + } + + // 检查 user_id 是否为 null + if (req.getUser_id() == null) { + throw new IllegalArgumentException("user_id cannot be null"); + } + + BeanUtils.copyProperties(req, instance); + + // 设置创建和更新时间 + LocalDateTime now = LocalDateTime.now(); + if (isNew) { + instance.setDeleted(false); + instance.setVersion(0); + // 如果created字段为空,设置为当前时间 + if (instance.getCreated() == null) { + instance.setCreated(now); + } + } else { + // 增加版本号 + instance.setVersion(instance.getVersion() + 1); + } + // 更新modified字段 + instance.setModified(now); + + saveAndFlush(instance); + + return instance; + } + + /** + * 根据ID查找实体 + * @param id 实体ID + * @return 实体对象 + */ + public Optional find(Long id) { + return repository.findById(id); + } + + /** + * 保存并刷新实体 + * @param entity 实体对象 + * @return 保存后的实体对象 + */ + public DailyNutritionSummary saveAndFlush(DailyNutritionSummary entity) { + return repository.saveAndFlush(entity); + } + + public List> getWeeklyNutritionSummary(Long userId, LocalDateTime startDate, LocalDateTime endDate) { + StringBuilder sql = new StringBuilder( + "SELECT DATE(date) as date, " + + "SUM(total_calories) as total_calories, " + + "SUM(total_protein) as total_protein, " + + "SUM(total_fat) as total_fat, " + + "SUM(total_carbs) as total_carbs " + + "FROM daily_nutrition_summary " + + "WHERE user_id = :userId " + + "AND DATE(date) >= DATE(:startDate) " + + "AND DATE(date) <= DATE(:endDate) " + + "AND deleted = 0 " + + "GROUP BY DATE(date) " + + "ORDER BY date ASC" + ); + + Map params = Map.of( + "userId", userId, + "startDate", startDate, + "endDate", endDate + ); + + return jdbcDao.queryForList(sql.toString(), params); + } + + public List> getWeeklyNutritionSummary(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " DATE(fi.date) as date," + + " SUM(fi.total_calories) as total_calories," + + " SUM(fi.total_protein) as total_protein," + + " SUM(fi.total_fat) as total_fat," + + " SUM(fi.total_carbs) as total_carbs" + + " FROM daily_nutrition_summary fi" + + " WHERE 1=1 AND fi.deleted = 0"); + + if (args != null) { + if (args.containsKey("user_id")) sql.append(" AND fi.user_id = :user_id"); + if (args.containsKey("start_date")) sql.append(" AND DATE(fi.date) >= DATE(:start_date)"); + if (args.containsKey("end_date")) sql.append(" AND DATE(fi.date) <= DATE(:end_date)"); + } + + sql.append(" GROUP BY DATE(fi.date)"); + sql.append(" ORDER BY DATE(fi.date) ASC"); + + return jdbcDao.queryForList(sql.toString(), args); + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/web/DailyNutritionSummaryController.java b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/web/DailyNutritionSummaryController.java new file mode 100644 index 0000000..7e4e7cd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/DailyNutritionSummary/web/DailyNutritionSummaryController.java @@ -0,0 +1,66 @@ +package com.ffii.fhsmsc.modules.DailyNutritionSummary.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.req.DailyNutritionSummaryReq; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.service.DailyNutritionSummaryService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/DailyNutritionSummary") +public class DailyNutritionSummaryController { + private DailyNutritionSummaryService DailyNutritionSummaryService; + + public DailyNutritionSummaryController( + DailyNutritionSummaryService DailyNutritionSummaryService + ) { + this.DailyNutritionSummaryService = DailyNutritionSummaryService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(DailyNutritionSummaryService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @GetMapping("/listByDate") + public RecordsRes> listByDate(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(DailyNutritionSummaryService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + .addDate("date") + .build())); + } + + @GetMapping("/listByWeek") + public RecordsRes> listByWeek(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(DailyNutritionSummaryService.getWeeklyNutritionSummary( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + .addDate("start_date") + .addDate("end_date") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid DailyNutritionSummaryReq req) { + return new IdRes(DailyNutritionSummaryService.saveOrUpdate(req).getId()); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperator.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperator.java new file mode 100644 index 0000000..67e8613 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperator.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.FB_Operator.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "FBOperator") + +public class FBOperator extends BaseEntity { + @Column(name = "FB_name") + private String FB_name; + + @Column(name="FB_operator_address") + private String FB_operator_address; + + @Column(name="FB_operator_phone") + private String FB_operator_phone; + + public String getFB_name() { + return FB_name; + } + public void setFB_name(String FB_name) { + this.FB_name = FB_name; + } + public String getFB_operator_address() { + return FB_operator_address; + } + public void setFB_operator_address(String FB_operator_address) { + this.FB_operator_address = FB_operator_address; + } + + public String getFB_operator_phone() { + return FB_operator_phone; + } + public void setFB_operator_phone(String FB_operator_phone) { + this.FB_operator_phone = FB_operator_phone; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperatorRepository.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperatorRepository.java new file mode 100644 index 0000000..3bbd6e8 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FBOperatorRepository.java @@ -0,0 +1,5 @@ +package com.ffii.fhsmsc.modules.FB_Operator.enity; +import com.ffii.core.support.AbstractRepository; +public interface FBOperatorRepository extends AbstractRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brand.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brand.java new file mode 100644 index 0000000..0e746bc --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brand.java @@ -0,0 +1,50 @@ +package com.ffii.fhsmsc.modules.FB_Operator.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "fb_brand") +//@AttributeOverride(name = "id", column = @Column(name = "FB_id")) +public class FB_brand extends BaseEntity { + // @Column(name = "FB_id", insertable = false, updatable = false) + @Column(name = "FB_operator_id") + private Long FB_operator_id; + + + @Column(name = "FB_name") + private String FB_name; + + @Column(name = "FB_type") + private String FB_type; + + @Column(name = "FB_cuisine") + private String FB_cuisine; + + public Long getFB_operator_id() { + return FB_operator_id; + } + public void setFB_operator_id(Long FB_operator_id) { + this.FB_operator_id = FB_operator_id; + } + public String getFB_name() { + return FB_name; + } + public void setFB_name(String FB_name) { + this.FB_name = FB_name; + } + public String getFB_type() { + return FB_type; + } + public void setFB_type(String FB_type) { + this.FB_type = FB_type; + } + public String getFB_cuisine() { + return FB_cuisine; + } + public void setFB_cuisine(String FB_cuisine) { + this.FB_cuisine = FB_cuisine; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brandRepository.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brandRepository.java new file mode 100644 index 0000000..493902e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/enity/FB_brandRepository.java @@ -0,0 +1,5 @@ +package com.ffii.fhsmsc.modules.FB_Operator.enity; +import com.ffii.core.support.AbstractRepository; +public interface FB_brandRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBCombinedSaveReq.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBCombinedSaveReq.java new file mode 100644 index 0000000..d191558 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBCombinedSaveReq.java @@ -0,0 +1,160 @@ +package com.ffii.fhsmsc.modules.FB_Operator.req; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FBCombinedSaveReq { + @JsonProperty("id") + private Long id; + + @JsonProperty("FB_operator_id") + private Long FB_operator_id; + + @JsonProperty("FB_id") + private Long FB_id; + + @JsonProperty("FB_name_en") + private String FB_name_en; + + @JsonProperty("FB_name_zh") + private String FB_name_zh; + + @JsonProperty("FB_type") + private String FB_type; + + @JsonProperty("FB_cuisine") + private String FB_cuisine; + + @JsonProperty("FB_outlet_district_en") + private String FB_outlet_district_en; + + @JsonProperty("FB_outlet_district_zh") + private String FB_outlet_district_zh; + + @JsonProperty("FB_outlet_address_en") + private String FB_outlet_address_en; + + @JsonProperty("FB_outlet_address_zh") + private String FB_outlet_address_zh; + + @JsonProperty("FB_outlet_phone") + private String FB_outlet_phone; + + @JsonProperty("User_Grp_Org_ID") + private String User_Grp_Org_ID; + + @JsonProperty("FB_type_en") + private String FB_type_en; + + @JsonProperty("FB_type_zh") + private String FB_type_zh; + + @JsonProperty("FB_cuisine_en") + private String FB_cuisine_en; + + @JsonProperty("FB_cuisine_zh") + private String FB_cuisine_zh; + + // Getters and setters + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_id() { + return FB_id; + } + public void setFB_id(Long FB_id) { + this.FB_id = FB_id; + } + public String getFB_name_en() { + return FB_name_en; + } + public void setFB_name_en(String FB_name_en) { + this.FB_name_en = FB_name_en; + } + public String getFB_name_zh() { + return FB_name_zh; + } + public void setFB_name_zh(String FB_name_zh) { + this.FB_name_zh = FB_name_zh; + } + public String getFB_type() { + return FB_type; + } + public void setFB_type(String FB_type) { + this.FB_type = FB_type; + } + public String getFB_cuisine() { + return FB_cuisine; + } + public void setFB_cuisine(String FB_cuisine) { + this.FB_cuisine = FB_cuisine; + } + public String getFB_outlet_district_en() { + return FB_outlet_district_en; + } + public void setFB_outlet_district_en(String FB_outlet_district_en) { + this.FB_outlet_district_en = FB_outlet_district_en; + } + public String getFB_outlet_district_zh() { + return FB_outlet_district_zh; + } + public void setFB_outlet_district_zh(String FB_outlet_district_zh) { + this.FB_outlet_district_zh = FB_outlet_district_zh; + } + public String getFB_outlet_address_en() { + return FB_outlet_address_en; + } + public void setFB_outlet_address_en(String FB_outlet_address_en) { + this.FB_outlet_address_en = FB_outlet_address_en; + } + public String getFB_outlet_address_zh() { + return FB_outlet_address_zh; + } + public void setFB_outlet_address_zh(String FB_outlet_address_zh) { + this.FB_outlet_address_zh = FB_outlet_address_zh; + } + public String getFB_outlet_phone() { + return FB_outlet_phone; + } + public void setFB_outlet_phone(String FB_outlet_phone) { + this.FB_outlet_phone = FB_outlet_phone; + } + public String getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + public void setUser_Grp_Org_ID(String User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } + public String getFB_type_en() { + return FB_type_en; + } + public void setFB_type_en(String FB_type_en) { + this.FB_type_en = FB_type_en; + } + public String getFB_type_zh() { + return FB_type_zh; + } + public void setFB_type_zh(String FB_type_zh) { + this.FB_type_zh = FB_type_zh; + } + public String getFB_cuisine_en() { + return FB_cuisine_en; + } + public void setFB_cuisine_en(String FB_cuisine_en) { + this.FB_cuisine_en = FB_cuisine_en; + } + public String getFB_cuisine_zh() { + return FB_cuisine_zh; + } + public void setFB_cuisine_zh(String FB_cuisine_zh) { + this.FB_cuisine_zh = FB_cuisine_zh; + } + public Long getFB_operator_id() { + return FB_operator_id; + } + public void setFB_operator_id(Long FB_operator_id) { + this.FB_operator_id = FB_operator_id; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBOperatorReq.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBOperatorReq.java new file mode 100644 index 0000000..a656be9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FBOperatorReq.java @@ -0,0 +1,94 @@ +package com.ffii.fhsmsc.modules.FB_Operator.req; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FBOperatorReq { + @JsonProperty("id") + private Long id; + + @JsonProperty("FB_operator_id") + private Long FB_operator_id; + + @JsonProperty("FB_name") + private String FB_name; + + @JsonProperty("FB_name_en") + private String FB_name_en; + + @JsonProperty("FB_name_zh") + private String FB_name_zh; + + @JsonProperty("FB_operator_address") + private String FB_operator_address; + + @JsonProperty("FB_operator_address_en") + private String FB_operator_address_en; + + @JsonProperty("FB_operator_address_zh") + private String FB_operator_address_zh; + + @JsonProperty("FB_operator_phone") + private String FB_operator_phone; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_operator_id() { + return FB_operator_id; + } + public void setFB_operator_id(Long FB_operator_id) { + this.FB_operator_id = FB_operator_id; + } + public String getFB_name() { + return FB_name; + } + public void setFB_name(String FB_name) { + this.FB_name = FB_name; + } + + public String getFB_name_en() { + return FB_name_en; + } + public void setFB_name_en(String FB_name_en) { + this.FB_name_en = FB_name_en; + } + public String getFB_name_zh() { + return FB_name_zh; + } + public void setFB_name_zh(String FB_name_zh) { + this.FB_name_zh = FB_name_zh; + } + public String getFB_operator_address() { + return FB_operator_address; + } + public void setFB_operator_address(String FB_operator_address) { + this.FB_operator_address = FB_operator_address; + } + public String getFB_operator_address_en() { + return FB_operator_address_en; + } + public void setFB_operator_address_en(String FB_operator_address_en) { + this.FB_operator_address_en = FB_operator_address_en; + } + public String getFB_operator_address_zh() { + return FB_operator_address_zh; + } + public void setFB_operator_address_zh(String FB_operator_address_zh) { + this.FB_operator_address_zh = FB_operator_address_zh; + } + public String getFB_operator_phone() { + return FB_operator_phone; + } + public void setFB_operator_phone(String FB_operator_phone) { + this.FB_operator_phone = FB_operator_phone; + } + + + + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FB_brandReq.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FB_brandReq.java new file mode 100644 index 0000000..e3b72cf --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/req/FB_brandReq.java @@ -0,0 +1,107 @@ +package com.ffii.fhsmsc.modules.FB_Operator.req; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FB_brandReq { + @JsonProperty("id") + private Long id; + + @JsonProperty("FB_operator_id") + private Long FB_operator_id; + + @JsonProperty("FB_name") + private String FB_name; + + @JsonProperty("FB_type") + private String FB_type; + + @JsonProperty("FB_cuisine") + private String FB_cuisine; + + @JsonProperty("FB_cuisine_en") + private String FB_cuisine_en; + + @JsonProperty("FB_cuisine_zh") + private String FB_cuisine_zh; + + @JsonProperty("FB_name_en") + private String FB_name_en; + + @JsonProperty("FB_name_zh") + private String FB_name_zh; + + @JsonProperty("FB_type_en") + private String FB_type_en; + + @JsonProperty("FB_type_zh") + private String FB_type_zh; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_operator_id() { + return FB_operator_id; + } + public void setFB_operator_id(Long FB_operator_id) { + this.FB_operator_id = FB_operator_id; + } + public String getFB_name() { + return FB_name; + } + public void setFB_name(String FB_name) { + this.FB_name = FB_name; + } + public String getFB_type() { + return FB_type; + } + public void setFB_type(String FB_type) { + this.FB_type = FB_type; + } + public String getFB_cuisine() { + return FB_cuisine; + } + public void setFB_cuisine(String FB_cuisine) { + this.FB_cuisine = FB_cuisine; + } + public String getFB_cuisine_en() { + return FB_cuisine_en; + } + public void setFB_cuisine_en(String FB_cuisine_en) { + this.FB_cuisine_en = FB_cuisine_en; + } + public String getFB_cuisine_zh() { + return FB_cuisine_zh; + } + public void setFB_cuisine_zh(String FB_cuisine_zh) { + this.FB_cuisine_zh = FB_cuisine_zh; + } + public String getFB_name_en() { + return FB_name_en; + } + public void setFB_name_en(String FB_name_en) { + this.FB_name_en = FB_name_en; + } + public String getFB_name_zh() { + return FB_name_zh; + } + public void setFB_name_zh(String FB_name_zh) { + this.FB_name_zh = FB_name_zh; + } + public String getFB_type_en() { + return FB_type_en; + } + public void setFB_type_en(String FB_type_en) { + this.FB_type_en = FB_type_en; + } + public String getFB_type_zh() { + return FB_type_zh; + } + public void setFB_type_zh(String FB_type_zh) { + this.FB_type_zh = FB_type_zh; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBCombinedService.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBCombinedService.java new file mode 100644 index 0000000..40d96c2 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBCombinedService.java @@ -0,0 +1,315 @@ +package com.ffii.fhsmsc.modules.FB_Operator.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.support.JdbcDao; +//import com.ffii.fhsmsc.modules.Combine.res.FBCombineRes; +import com.ffii.fhsmsc.modules.FB_Operator.enity.FB_brand; +import com.ffii.fhsmsc.modules.FB_Operator.req.FBCombinedSaveReq; +import com.ffii.fhsmsc.modules.FB_Operator.req.FB_brandReq; +import com.ffii.fhsmsc.modules.FB_Outlets.enity.FB_outlets; +import com.ffii.fhsmsc.modules.FB_Outlets.req.FB_outletsReq; +import com.ffii.fhsmsc.modules.FB_Outlets.service.FB_outletsService; +import com.ffii.fhsmsc.modules.I18n.enity.i18n; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.service.fb_operator_and_organization_relationshipService; +import com.ffii.fhsmsc.modules.Combine.res.FBCombinedListRes; + +@Service +public class FBCombinedService { + private static final Logger logger = LoggerFactory.getLogger(FBCombinedService.class); + + private final FB_brandService fbBrandService; + private final FB_outletsService fbOutletsService; + private final i18nService i18nService; + private final fb_operator_and_organization_relationshipService fb_operator_and_organization_relationshipService; + private final JdbcDao jdbcDao; + + @Autowired + public FBCombinedService( + FB_brandService fbBrandService, + FB_outletsService fbOutletsService, + i18nService i18nService, + fb_operator_and_organization_relationshipService fb_operator_and_organization_relationshipService, + JdbcDao jdbcDao) { + this.fbBrandService = fbBrandService; + this.fbOutletsService = fbOutletsService; + this.i18nService = i18nService; + this.fb_operator_and_organization_relationshipService = fb_operator_and_organization_relationshipService; + this.jdbcDao = jdbcDao; + } + + @Transactional(rollbackFor = Exception.class) + public Long saveCombined(FBCombinedSaveReq req) { + logger.info("Received combined save request: {}", req); + + // Step 1: Save to fb_brand + FB_brandReq fbBrandReq = new FB_brandReq(); + fbBrandReq.setId(req.getId()); + fbBrandReq.setFB_operator_id(req.getFB_operator_id()); + fbBrandReq.setFB_name_en(req.getFB_name_en()); + fbBrandReq.setFB_name_zh(req.getFB_name_zh()); + fbBrandReq.setFB_type_en(req.getFB_type_en()); + fbBrandReq.setFB_type_zh(req.getFB_type_zh()); + fbBrandReq.setFB_cuisine_en(req.getFB_cuisine_en()); + fbBrandReq.setFB_cuisine_zh(req.getFB_cuisine_zh()); + + FB_brand fbBrand = fbBrandService.saveOrUpdate(fbBrandReq); + Long fbBrandId = fbBrand.getId(); + logger.info("Saved FB_brand with id: {}", fbBrandId); + + // Step 2: Save to fb_outlets + FB_outletsReq fbOutletsReq = new FB_outletsReq(); + fbOutletsReq.setId(req.getId()); + fbOutletsReq.setFB_id(fbBrandId); + fbOutletsReq.setFB_outlet_district_en(req.getFB_outlet_district_en()); + fbOutletsReq.setFB_outlet_district_zh(req.getFB_outlet_district_zh()); + fbOutletsReq.setFB_outlet_address_en(req.getFB_outlet_address_en()); + fbOutletsReq.setFB_outlet_address_zh(req.getFB_outlet_address_zh()); + fbOutletsReq.setFB_outlet_phone(req.getFB_outlet_phone()); + + FB_outlets fbOutlets = fbOutletsService.saveOrUpdate(fbOutletsReq); + Long fbOutletsId = fbOutlets.getId(); + logger.info("Saved FB_outlets with id: {}", fbOutletsId); + + return fbBrandId; + } + + public List listCombined() { + logger.info("Received combined list request"); + + List result = new ArrayList<>(); + + // Step 1: Get all operators + String operatorSql = "SELECT * FROM fboperator"; + List> operators = jdbcDao.queryForList(operatorSql); + logger.info("Found {} operators", operators.size()); + + for (Map operator : operators) { + FBCombinedListRes combinedRes = new FBCombinedListRes(); + + // Set operator info + FBCombinedListRes.OperatorInfo operatorInfo = new FBCombinedListRes.OperatorInfo(); + operatorInfo.setId(((Number) operator.get("id")).longValue()); + operatorInfo.setFB_name((String) operator.get("FB_name")); + operatorInfo.setFB_operator_address((String) operator.get("FB_operator_address")); + operatorInfo.setFB_operator_phone((String) operator.get("FB_operator_phone")); + + // Get User_Grp_Org_ID + Map relationshipArgs = new HashMap<>(); + relationshipArgs.put("FB_operator_id", operatorInfo.getId()); + List> relationships = fb_operator_and_organization_relationshipService.search(relationshipArgs); + String userGrpOrgId = relationships.isEmpty() ? null : relationships.get(0).get("User_Grp_Org_ID").toString(); + operatorInfo.setUser_Grp_Org_ID(userGrpOrgId); + + combinedRes.setOperator(operatorInfo); + + // Get brands for this operator + Map brandArgs = new HashMap<>(); + brandArgs.put("FB_operator_id", operatorInfo.getId()); + List> brands = fbBrandService.search(brandArgs); + logger.info("Found {} brands for operator id: {}", brands.size(), operatorInfo.getId()); + + List brandInfoList = new ArrayList<>(); + for (Map brand : brands) { + Long fbBrandId = ((Number) brand.get("id")).longValue(); + FBCombinedListRes.BrandInfo brandInfo = new FBCombinedListRes.BrandInfo(); + brandInfo.setFb_id(fbBrandId); + + // Get i18n translations for brand + Map i18nBrandArgs = new HashMap<>(); + i18nBrandArgs.put("table_name", "fb_brand"); + i18nBrandArgs.put("record_id", fbBrandId); + + // Get FB_name translations + i18nBrandArgs.put("field_name", "FB_name"); + List> nameTranslations = i18nService.search(i18nBrandArgs); + Map nameTranslationMap = nameTranslations.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("language"), + entry -> (String) entry.get("value"), + (v1, v2) -> v1 + )); + + // Get FB_type translations + i18nBrandArgs.put("field_name", "FB_type"); + List> typeTranslations = i18nService.search(i18nBrandArgs); + Map typeTranslationMap = typeTranslations.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("language"), + entry -> (String) entry.get("value"), + (v1, v2) -> v1 + )); + + // Get FB_cuisine translations + i18nBrandArgs.put("field_name", "FB_cuisine"); + List> cuisineTranslations = i18nService.search(i18nBrandArgs); + Map cuisineTranslationMap = cuisineTranslations.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("language"), + entry -> (String) entry.get("value"), + (v1, v2) -> v1 + )); + + brandInfo.setFb_name_en(nameTranslationMap.getOrDefault("en", (String) brand.get("FB_name"))); + brandInfo.setFb_name_zh(nameTranslationMap.getOrDefault("zh", (String) brand.get("FB_name"))); + brandInfo.setFb_type_en(typeTranslationMap.getOrDefault("en", (String) brand.get("FB_type"))); + brandInfo.setFb_type_zh(typeTranslationMap.getOrDefault("zh", (String) brand.get("FB_type"))); + brandInfo.setFb_cuisine_en(cuisineTranslationMap.getOrDefault("en", (String) brand.get("FB_cuisine"))); + brandInfo.setFb_cuisine_zh(cuisineTranslationMap.getOrDefault("zh", (String) brand.get("FB_cuisine"))); + + // Get outlets for this brand + Map outletArgs = new HashMap<>(); + outletArgs.put("FB_id", fbBrandId); + List> outlets = fbOutletsService.search(outletArgs); + logger.info("Found {} outlets for brand id: {}", outlets.size(), fbBrandId); + + List outletInfoList = new ArrayList<>(); + for (Map outlet : outlets) { + Long outletId = ((Number) outlet.get("id")).longValue(); + FBCombinedListRes.OutletInfo outletInfo = new FBCombinedListRes.OutletInfo(); + outletInfo.setId(outletId); + + // Get i18n translations for outlet + Map i18nOutletArgs = new HashMap<>(); + i18nOutletArgs.put("table_name", "fb_outlets"); + i18nOutletArgs.put("record_id", outletId); + + // District translations + i18nOutletArgs.put("field_name", "FB_outlet_district"); + List> districtTranslations = i18nService.search(i18nOutletArgs); + Map districtTranslationMap = districtTranslations.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("language"), + entry -> (String) entry.get("value"), + (v1, v2) -> v1 + )); + + // Address translations + i18nOutletArgs.put("field_name", "FB_outlet_address"); + List> addressTranslations = i18nService.search(i18nOutletArgs); + Map addressTranslationMap = addressTranslations.stream() + .collect(Collectors.toMap( + entry -> (String) entry.get("language"), + entry -> (String) entry.get("value"), + (v1, v2) -> v1 + )); + + outletInfo.setFb_outlet_district_en(districtTranslationMap.getOrDefault("en", (String) outlet.get("FB_outlet_district"))); + outletInfo.setFb_outlet_district_zh(districtTranslationMap.getOrDefault("zh", (String) outlet.get("FB_outlet_district"))); + outletInfo.setFb_outlet_address_en(addressTranslationMap.getOrDefault("en", (String) outlet.get("FB_outlet_address"))); + outletInfo.setFb_outlet_address_zh(addressTranslationMap.getOrDefault("zh", (String) outlet.get("FB_outlet_address"))); + outletInfo.setFb_outlet_phone((String) outlet.get("FB_outlet_phone")); + + outletInfoList.add(outletInfo); + } + + brandInfo.setOutlets(outletInfoList); + brandInfoList.add(brandInfo); + } + + combinedRes.setBrands(brandInfoList); + result.add(combinedRes); + } + + logger.info("Returning {} combined list entries", result.size()); + return result; + } + + @Transactional(rollbackFor = Exception.class) + public void deleteCombined(Long fbId) { + logger.info("Starting deletion process for FB_id: {}", fbId); + + try { + // 1. 首先检查是否存在 + Map checkParams = new HashMap<>(); + checkParams.put("id", fbId); + List> brands = fbBrandService.search(checkParams); + + if (brands.isEmpty()) { + throw new RuntimeException("Brand not found"); + } + + // 2. 删除相关的门店记录 + String outletSql = "DELETE FROM fb_outlets WHERE FB_id = :FB_id"; + Map outletParams = new HashMap<>(); + outletParams.put("FB_id", fbId); + jdbcDao.executeUpdate(outletSql, outletParams); + logger.info("Deleted outlets for FB_id: {}", fbId); + + // 3. 删除相关的 i18n 记录 + String i18nSql = "DELETE FROM i18n WHERE table_name = 'fb_brand' AND record_id = :FB_id"; + Map i18nParams = new HashMap<>(); + i18nParams.put("FB_id", fbId); + jdbcDao.executeUpdate(i18nSql, i18nParams); + logger.info("Deleted i18n entries for FB_id: {}", fbId); + + // 4. 删除品牌记录 + String brandSql = "DELETE FROM fb_brand WHERE id = :id"; + Map brandParams = new HashMap<>(); + brandParams.put("id", fbId); + jdbcDao.executeUpdate(brandSql, brandParams); + logger.info("Deleted brand with id: {}", fbId); + + // 5. 删除组织关系记录 + String relationshipSql = "DELETE FROM fb_operator_and_organization_relationship WHERE FB_id = :FB_id"; + Map relationshipParams = new HashMap<>(); + relationshipParams.put("FB_id", fbId); + jdbcDao.executeUpdate(relationshipSql, relationshipParams); + logger.info("Deleted organization relationships for FB_id: {}", fbId); + + } catch (Exception e) { + logger.error("Error during deletion process for FB_id: {}", fbId, e); + throw new RuntimeException("Failed to delete combined data", e); + } + } + + @Transactional(rollbackFor = Exception.class) + public void deleteOutlet(Long fbId, Long outletId) { + logger.info("Starting deletion process for outlet: FB_id={}, ", fbId, outletId); + + try { + // 1. 检查门店是否存在 + Map checkParams = new HashMap<>(); + checkParams.put("FB_id", fbId); + checkParams.put("id", outletId); + String checkSql = "SELECT COUNT(*) FROM fb_outlets WHERE FB_id = :FB_id AND id = :id"; + int count = jdbcDao.queryForInt(checkSql, checkParams); + + if (count == 0) { + throw new RuntimeException("Outlet not found"); + } + + // 2. 删除门店的 i18n 记录 + String i18nSql = "DELETE FROM i18n WHERE table_name = 'fb_outlets' AND record_id = :outletId"; + Map i18nParams = new HashMap<>(); + i18nParams.put("outletId", outletId); + jdbcDao.executeUpdate(i18nSql, i18nParams); + logger.info("Deleted i18n entries for outlet_id: {}", outletId); + + // 3. 删除门店记录 + String outletSql = "DELETE FROM fb_outlets WHERE FB_id = :FB_id AND id = :id"; + Map outletParams = new HashMap<>(); + outletParams.put("FB_id", fbId); + outletParams.put("id", outletId); + jdbcDao.executeUpdate(outletSql, outletParams); + logger.info("Deleted outlet: FB_id={}, ", fbId, outletId); + + } catch (Exception e) { + logger.error("Error during outlet deletion process: FB_id={}, ", fbId, outletId, e); + throw new RuntimeException("Failed to delete outlet", e); + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBOperatorService.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBOperatorService.java new file mode 100644 index 0000000..e6bf001 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FBOperatorService.java @@ -0,0 +1,143 @@ +package com.ffii.fhsmsc.modules.FB_Operator.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.FB_Operator.enity.FBOperator; +import com.ffii.fhsmsc.modules.FB_Operator.enity.FBOperatorRepository; +import com.ffii.fhsmsc.modules.FB_Operator.req.FBOperatorReq; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; + +import jakarta.validation.Valid; + +@Service +public class FBOperatorService extends AbstractBaseEntityService { + + private static final Logger logger = LoggerFactory.getLogger(FBOperatorService.class); + + private final i18nService i18nService; + + public FBOperatorService(JdbcDao jdbcDao, FBOperatorRepository repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT fb.* FROM `fboperator` fb WHERE 1=1"); + + if (args != null) { + if (args.containsKey("id")) sql.append(" AND fb.id = :id"); + if (args.containsKey("FB_name")) sql.append(" AND fb.FB_name LIKE :FB_name"); + } + + sql.append(" ORDER BY fb.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public FBOperator saveOrUpdate(@Valid FBOperatorReq req) { + logger.info("Request: id={}, FB_name={}, FB_operator_address={}, FB_operator_phone={}", + req.getId(), req.getFB_name(), req.getFB_operator_address(), req.getFB_operator_phone()); + + FBOperator instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(() -> new InternalServerErrorException("Operator not found for id: " + req.getId())); + logger.info("Found existing operator: id={}, FB_name={}", instance.getId(), instance.getFB_name()); + } else { + instance = new FBOperator(); + isNew = true; + logger.info("Creating new operator"); + } + + // 设置字段 + instance.setFB_name(req.getFB_name_en()); + instance.setFB_operator_address(req.getFB_operator_address_en()); + instance.setFB_operator_phone(req.getFB_operator_phone()); + + if (isNew) { + instance.setCreated(LocalDateTime.now()); + } + instance.setModified(LocalDateTime.now()); + + try { + instance = repository.saveAndFlush(instance); + logger.info("After save - Saved operator: id={}, FB_name={}", + instance.getId(), instance.getFB_name()); + + Long operatorId = instance.getId(); + updateI18nEntry("fboperator", "FB_name", operatorId, "en", req.getFB_name_en()); + updateI18nEntry("fboperator", "FB_name", operatorId, "zh", req.getFB_name_zh()); + updateI18nEntry("fboperator", "FB_operator_address", operatorId, "en", req.getFB_operator_address_en()); + updateI18nEntry("fboperator", "FB_operator_address", operatorId, "zh", req.getFB_operator_address_zh()); + } catch (Exception e) { + logger.error("Failed to save FBOperator: id={}, error={}", req.getId(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save operator: " + e.getMessage()); + } + + return instance; + } + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + + public List findAll() { + return repository.findAll(); + } + + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long id) { + logger.info("Deleting operator with id: {}", id); + String sql = "DELETE FROM fboperator WHERE id = :id"; + Map args = new HashMap<>(); + args.put("id", id); + jdbcDao.queryForList(sql, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FB_brandService.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FB_brandService.java new file mode 100644 index 0000000..6374cfb --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/service/FB_brandService.java @@ -0,0 +1,200 @@ +package com.ffii.fhsmsc.modules.FB_Operator.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.FB_Operator.enity.FB_brand; +import com.ffii.fhsmsc.modules.FB_Operator.enity.FB_brandRepository; +import com.ffii.fhsmsc.modules.FB_Operator.req.FB_brandReq; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; + +import jakarta.validation.Valid; + +@Service +public class FB_brandService extends AbstractBaseEntityService { + + private static final Logger logger = LoggerFactory.getLogger(FB_brandService.class); + + private final i18nService i18nService; + + public FB_brandService(JdbcDao jdbcDao, FB_brandRepository repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fb.*" + + " FROM `fb_brand` fb" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("id")) sql.append(" AND fb.id = :id"); + if (args.containsKey("FB_operator_id")) sql.append(" AND fb.FB_operator_id = :FB_operator_id"); + if (args.containsKey("FB_name")) sql.append(" AND fb.FB_name LIKE :FB_name"); + if (args.containsKey("FB_type")) sql.append(" AND fb.FB_type = :FB_type"); + } + + sql.append(" ORDER BY fb.id"); + + List> brands = jdbcDao.queryForList(sql.toString(), args); + + for (Map brand : brands) { + Long brandId = ((Number) brand.get("id")).longValue(); + + // 获取所有字段的翻译 + String[] fields = {"FB_name", "FB_type", "FB_cuisine"}; + String[] languages = {"en", "zh"}; + + for (String field : fields) { + Map fieldArgs = new HashMap<>(); + fieldArgs.put("table_name", "fb_brand"); + fieldArgs.put("field_name", field); + fieldArgs.put("record_id", brandId); + List> translations = i18nService.search(fieldArgs); + + // 添加翻译到结果中 + for (Map translation : translations) { + String language = (String) translation.get("language"); + String value = (String) translation.get("value"); + brand.put(field + "_" + language, value); + } + + // 如果没有找到翻译,使用默认值 + for (String lang : languages) { + if (!brand.containsKey(field + "_" + lang)) { + brand.put(field + "_" + lang, brand.get(field)); + } + } + } + } + + return brands; + } + + @Transactional(rollbackFor = Exception.class) + public FB_brand saveOrUpdate(@Valid FB_brandReq req) { + logger.info("Request: id={}, FB_operator_id={}, FB_name={}, FB_type={}, FB_cuisine={}", + req.getId(), req.getFB_operator_id(), req.getFB_name(), req.getFB_type(), req.getFB_cuisine()); + + FB_brand instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(() -> new InternalServerErrorException("Brand not found for id: " + req.getId())); + logger.info("Found existing brand: id={}, FB_name={}", instance.getId(), instance.getFB_name()); + } else { + instance = new FB_brand(); + isNew = true; + logger.info("Creating new brand"); + } + + // 手动设置字段 + instance.setId(req.getId()); + instance.setFB_operator_id(req.getFB_operator_id()); + instance.setFB_name(req.getFB_name_en()); + instance.setFB_type(req.getFB_type_en()); + instance.setFB_cuisine(req.getFB_cuisine_en()); + + logger.info("Before save - Setting values: FB_name={}, FB_type={}, FB_cuisine={}", + instance.getFB_name(), instance.getFB_type(), instance.getFB_cuisine()); + + if (isNew) { + instance.setCreated(LocalDateTime.now()); + } + instance.setModified(LocalDateTime.now()); + + try { + instance = repository.saveAndFlush(instance); + logger.info("After save - Saved brand: id={}, FB_name={}, FB_type={}, FB_cuisine={}", + instance.getId(), instance.getFB_name(), instance.getFB_type(), instance.getFB_cuisine()); + } catch (Exception e) { + logger.error("Failed to save FB_Operator: id={}, error={}", req.getId(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save brand: " + e.getMessage()); + } + + Long operatorId = instance.getId(); + logger.info("Processing i18n entries for operatorId={}", operatorId); + + // 更新 i18n 记录 + try { + updateI18nEntry("fb_brand", "FB_name", operatorId, "en", req.getFB_name_en()); + updateI18nEntry("fb_brand", "FB_name", operatorId, "zh", req.getFB_name_zh()); + + updateI18nEntry("fb_brand", "FB_type", operatorId, "en", req.getFB_type_en()); + updateI18nEntry("fb_brand", "FB_type", operatorId, "zh", req.getFB_type_zh()); + + updateI18nEntry("fb_brand", "FB_cuisine", operatorId, "en", req.getFB_cuisine_en()); + updateI18nEntry("fb_brand", "FB_cuisine", operatorId, "zh", req.getFB_cuisine_zh()); + + logger.info("Successfully updated all i18n entries"); + } catch (Exception e) { + logger.error("Failed to update i18n entries: error={}", e.getMessage(), e); + throw new InternalServerErrorException("Failed to update i18n entries: " + e.getMessage()); + } + + return instance; + } + + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + + public List findAll() { + return repository.findAll(); + } + + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long id) { + logger.info("Deleting brand with id: {}", id); + String sql = "DELETE FROM fb_brand WHERE id = :id"; + Map args = new HashMap<>(); + args.put("id", id); + jdbcDao.queryForList(sql, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBCombinedController.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBCombinedController.java new file mode 100644 index 0000000..baca76c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBCombinedController.java @@ -0,0 +1,74 @@ +package com.ffii.fhsmsc.modules.FB_Operator.web; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.fhsmsc.modules.FB_Operator.req.FBCombinedSaveReq; +import com.ffii.fhsmsc.modules.FB_Operator.service.FBCombinedService; +import com.ffii.fhsmsc.modules.Combine.res.FBCombinedListRes; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/combined") +public class FBCombinedController { + private static final Logger logger = LoggerFactory.getLogger(FBCombinedController.class); + private final FBCombinedService fbCombinedService; + + public FBCombinedController(FBCombinedService fbCombinedService) { + this.fbCombinedService = fbCombinedService; + } + + @PostMapping("/save") + public IdRes saveCombined(@RequestBody @Valid FBCombinedSaveReq req) { + try { + logger.info("Received combined save request: {}", req); + Long fbOperatorId = fbCombinedService.saveCombined(req); + logger.info("Successfully processed combined save request"); + return new IdRes(fbOperatorId); + } catch (Exception e) { + logger.error("Error processing combined save request", e); + throw e; + } + } + + @GetMapping("/list") + public List listCombined() { + try { + logger.info("Received combined list request"); + List result = fbCombinedService.listCombined(); + logger.info("Successfully processed combined list request"); + return result; + } catch (Exception e) { + logger.error("Error processing combined list request", e); + throw e; + } + } + + @DeleteMapping("/delete/{id}") + public void deleteCombined(@PathVariable Long id) { + try { + logger.info("Received combined delete request for id: {}", id); + fbCombinedService.deleteCombined(id); + logger.info("Successfully processed combined delete request"); + } catch (Exception e) { + logger.error("Error processing combined delete request", e); + throw e; + } + } + + @DeleteMapping("/deleteOutlet/{fbId}/{outletId}") + public void deleteOutlet(@PathVariable Long fbId, @PathVariable Long outletId) { + fbCombinedService.deleteOutlet(fbId, outletId); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FB_brandController.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FB_brandController.java new file mode 100644 index 0000000..34d48e0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FB_brandController.java @@ -0,0 +1,51 @@ +package com.ffii.fhsmsc.modules.FB_Operator.web; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.FB_Operator.req.FB_brandReq; +import com.ffii.fhsmsc.modules.FB_Operator.service.FB_brandService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/FB_brand") +public class FB_brandController { + private static final Logger logger = LoggerFactory.getLogger(FB_brandController.class); + private FB_brandService FB_brandService; + + public FB_brandController(FB_brandService FB_brandService) { + this.FB_brandService = FB_brandService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(FB_brandService.search( + CriteriaArgsBuilder.withRequest(request) + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid FB_brandReq req) { + try { + logger.info("Received save request: {}", req); + IdRes result = new IdRes(FB_brandService.saveOrUpdate(req).getId()); + logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + logger.error("Error processing save request", e); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBoperatorController.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBoperatorController.java new file mode 100644 index 0000000..4b17f44 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Operator/web/FBoperatorController.java @@ -0,0 +1,51 @@ +package com.ffii.fhsmsc.modules.FB_Operator.web; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.FB_Operator.req.FBOperatorReq; +import com.ffii.fhsmsc.modules.FB_Operator.service.FBOperatorService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/FB_operator") +public class FBoperatorController { + private static final Logger logger = LoggerFactory.getLogger(FBoperatorController.class); + private final FBOperatorService fbOperatorService; + + public FBoperatorController(FBOperatorService fbOperatorService) { + this.fbOperatorService = fbOperatorService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fbOperatorService.search( + CriteriaArgsBuilder.withRequest(request) + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid FBOperatorReq req) { + try { + logger.info("Received save request: {}", req); + IdRes result = new IdRes(fbOperatorService.saveOrUpdate(req).getId()); + logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + logger.error("Error processing save request", e); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outlets.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outlets.java new file mode 100644 index 0000000..550b889 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outlets.java @@ -0,0 +1,53 @@ +package com.ffii.fhsmsc.modules.FB_Outlets.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Id; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +/** @author Terence */ +@Entity +@Table(name = "FB_Outlets") +public class FB_outlets extends BaseEntity { + @Column + private Long FB_id; + @Column + private String FB_outlet_district; + @Column + private String FB_outlet_address; + @Column + private String FB_outlet_phone; + + + public Long getFB_id() { + return FB_id; + } + public void setFB_id(Long FB_id) { + this.FB_id = FB_id; + } + public String getFB_outlet_district() { + return FB_outlet_district; + } + public void setFB_outlet_district(String FB_outlet_district) { + this.FB_outlet_district = FB_outlet_district; + } + public String getFB_outlet_address() { + return FB_outlet_address; + } + public void setFB_outlet_address(String FB_outlet_address) { + this.FB_outlet_address = FB_outlet_address; + } + public String getFB_outlet_phone() { + return FB_outlet_phone; + } + public void setFB_outlet_phone(String FB_outlet_phone) { + this.FB_outlet_phone = FB_outlet_phone; + } + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outletsRepository.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outletsRepository.java new file mode 100644 index 0000000..b7ac20a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/enity/FB_outletsRepository.java @@ -0,0 +1,5 @@ +package com.ffii.fhsmsc.modules.FB_Outlets.enity; +import com.ffii.core.support.AbstractRepository; +public interface FB_outletsRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/req/FB_outletsReq.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/req/FB_outletsReq.java new file mode 100644 index 0000000..4c485be --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/req/FB_outletsReq.java @@ -0,0 +1,90 @@ +package com.ffii.fhsmsc.modules.FB_Outlets.req; +import com.fasterxml.jackson.annotation.JsonProperty; +public class FB_outletsReq { + + @JsonProperty("id") + private Long id; + + @JsonProperty("FB_id") + private Long FB_id; + + @JsonProperty("FB_outlet_district") + private String FB_outlet_district; + + @JsonProperty("FB_outlet_address") + private String FB_outlet_address; + + @JsonProperty("FB_outlet_phone") + private String FB_outlet_phone; + + @JsonProperty("FB_outlet_district_en") + private String FB_outlet_district_en; + + @JsonProperty("FB_outlet_district_zh") + private String FB_outlet_district_zh; // 新增 + + @JsonProperty("FB_outlet_address_en") + private String FB_outlet_address_en; // 新增 + + @JsonProperty("FB_outlet_address_zh") + private String FB_outlet_address_zh; + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + public Long getFB_id() { + return FB_id; + } + public void setFB_id(Long FB_id) { + this.FB_id = FB_id; + } + public String getFB_outlet_district() { + return FB_outlet_district; + } + public void setFB_outlet_district(String FB_outlet_district) { + this.FB_outlet_district = FB_outlet_district; + } + public String getFB_outlet_address() { + return FB_outlet_address; + } + public void setFB_outlet_address(String FB_outlet_address) { + this.FB_outlet_address = FB_outlet_address; + } + public String getFB_outlet_phone() { + return FB_outlet_phone; + } + public void setFB_outlet_phone(String FB_outlet_phone) { + this.FB_outlet_phone = FB_outlet_phone; + } + public String getFB_outlet_district_en() { + return FB_outlet_district_en; + } + public void setFB_outlet_district_en(String FB_outlet_district_en) { + this.FB_outlet_district_en = FB_outlet_district_en; + } + public String getFB_outlet_district_zh() { + return FB_outlet_district_zh; + } + public void setFB_outlet_district_zh(String FB_outlet_district_zh) { + this.FB_outlet_district_zh = FB_outlet_district_zh; + } + public String getFB_outlet_address_en() { + return FB_outlet_address_en; + } + public void setFB_outlet_address_en(String FB_outlet_address_en) { + this.FB_outlet_address_en = FB_outlet_address_en; + } + public String getFB_outlet_address_zh() { + return FB_outlet_address_zh; + } + public void setFB_outlet_address_zh(String FB_outlet_address_zh) { + this.FB_outlet_address_zh = FB_outlet_address_zh; + } + + + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/service/FB_outletsService.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/service/FB_outletsService.java new file mode 100644 index 0000000..3036dfd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/service/FB_outletsService.java @@ -0,0 +1,195 @@ +package com.ffii.fhsmsc.modules.FB_Outlets.service; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.FB_Outlets.enity.FB_outlets; +import com.ffii.fhsmsc.modules.FB_Outlets.enity.FB_outletsRepository; +import com.ffii.fhsmsc.modules.FB_Outlets.req.FB_outletsReq; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; + +import jakarta.validation.Valid; + +@Service +public class FB_outletsService extends AbstractBaseEntityService { + private static final Logger logger = LoggerFactory.getLogger(FB_outletsService.class); + private final i18nService i18nService; + public FB_outletsService(JdbcDao jdbcDao, FB_outletsRepository repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fo.*" + + " FROM `fb_outlets` fo" + + " WHERE 1=1"); + if (args.containsKey("FB_id")) { + sql.append(" AND fo.FB_id = :FB_id"); + } + + List> outlets = jdbcDao.queryForList(sql.toString(), args); + + for (Map outlet : outlets) { + Long outletId = ((Number) outlet.get("id")).longValue(); + + Map districtArgs = new HashMap<>(); + districtArgs.put("table_name", "fb_outlets"); + districtArgs.put("field_name", "FB_outlet_district"); + districtArgs.put("record_id", outletId); + List> districtTranslations = i18nService.search(districtArgs); + + Map addressArgs = new HashMap<>(); + addressArgs.put("table_name", "fb_outlets"); + addressArgs.put("field_name", "FB_outlet_address"); + addressArgs.put("record_id", outletId); + List> addressTranslations = i18nService.search(addressArgs); + + for (Map translation : districtTranslations) { + String language = (String) translation.get("language"); + String value = (String) translation.get("value"); + outlet.put("FB_outlet_district_" + language, value); + } + + for (Map translation : addressTranslations) { + String language = (String) translation.get("language"); + String value = (String) translation.get("value"); + outlet.put("FB_outlet_address_" + language, value); + } + } + + return outlets; + } + + @Transactional(rollbackFor = Exception.class) + public FB_outlets saveOrUpdate(@Valid FB_outletsReq req) { + logger.info("Request: id={}, FB_id={}, FB_outlet_district_en={}, FB_outlet_address_en={}, FB_outlet_phone={}", + req.getId(), req.getFB_id(), req.getFB_outlet_district_en(), req.getFB_outlet_address_en(), req.getFB_outlet_phone()); + + FB_outlets instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(() -> new InternalServerErrorException("Outlet not found for id: " + req.getId())); + logger.info("Found existing outlet: id={}, FB_outlet_district={}, FB_outlet_address={}", + instance.getId(), instance.getFB_outlet_district(), instance.getFB_outlet_address()); + } else { + instance = new FB_outlets(); + isNew = true; + logger.info("Creating new outlet"); + } + + // Set basic fields + instance.setFB_id(req.getFB_id()); + instance.setFB_outlet_phone(req.getFB_outlet_phone()); + + // 使用英文版本作为默认值 + String districtEn = req.getFB_outlet_district_en(); + String addressEn = req.getFB_outlet_address_en(); + + logger.info("Setting values - districtEn={}, addressEn={}", districtEn, addressEn); + + instance.setFB_outlet_district(districtEn); + instance.setFB_outlet_address(addressEn); + + logger.info("Before save - Setting values: FB_outlet_district={}, FB_outlet_address={}", + instance.getFB_outlet_district(), instance.getFB_outlet_address()); + + if (isNew) { + instance.setCreated(LocalDateTime.now()); + } + instance.setModified(LocalDateTime.now()); + + // Set createdBy and modifiedBy to null + instance.setCreatedBy(null); + instance.setModifiedBy(null); + + try { + instance = repository.saveAndFlush(instance); + logger.info("After save - Saved outlet: id={}, FB_outlet_district={}, FB_outlet_address={}", + instance.getId(), instance.getFB_outlet_district(), instance.getFB_outlet_address()); + } catch (Exception e) { + logger.error("Failed to save FB_outlets: id={}, error={}", req.getId(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save outlet: " + e.getMessage()); + } + + Long outletId = instance.getId(); + logger.info("Processing i18n entries for outletId={}", outletId); + + // Update i18n records + try { + updateI18nEntry("fb_outlets", "FB_outlet_district", outletId, "en", districtEn); + updateI18nEntry("fb_outlets", "FB_outlet_district", outletId, "zh", req.getFB_outlet_district_zh()); + updateI18nEntry("fb_outlets", "FB_outlet_address", outletId, "en", addressEn); + updateI18nEntry("fb_outlets", "FB_outlet_address", outletId, "zh", req.getFB_outlet_address_zh()); + logger.info("Successfully updated all i18n entries"); + } catch (Exception e) { + logger.error("Failed to update i18n entries: error={}", e.getMessage(), e); + throw new InternalServerErrorException("Failed to update i18n entries: " + e.getMessage()); + } + + return instance; + } + + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + + public List findAll() { + return repository.findAll(); + } + @Transactional(rollbackFor = Exception.class) + public void deleteByFBId(Long fbId) { + logger.info("Deleting operator with FB_id: {}", fbId); + String sql = "DELETE FROM fb_operator WHERE FB_id = :FB_id"; + Map args = new HashMap<>(); + args.put("FB_id", fbId); + jdbcDao.queryForList(sql, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/web/FB_outletsController.java b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/web/FB_outletsController.java new file mode 100644 index 0000000..0a40fdf --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/FB_Outlets/web/FB_outletsController.java @@ -0,0 +1,55 @@ +package com.ffii.fhsmsc.modules.FB_Outlets.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.FB_Outlets.req.FB_outletsReq; +import com.ffii.fhsmsc.modules.FB_Outlets.service.FB_outletsService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/FB_outlets") +public class FB_outletsController { + private FB_outletsService FB_outletsService; + + public FB_outletsController( + FB_outletsService FB_outletsService + ) { + this.FB_outletsService = FB_outletsService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(FB_outletsService.search( + CriteriaArgsBuilder.withRequest(request) + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid FB_outletsReq req) { + try { + //logger.info("Received save request: {}", req); + IdRes result = new IdRes(FB_outletsService.saveOrUpdate(req).getId()); + //logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + //logger.error("Error processing save request", e); + throw e; + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18n.java b/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18n.java new file mode 100644 index 0000000..74db17a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18n.java @@ -0,0 +1,61 @@ +package com.ffii.fhsmsc.modules.I18n.enity; +import java.time.LocalDateTime; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import com.ffii.core.entity.BaseEntity; +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; + +/** @author Terence */ +@Entity +@Table(name = "i18n") +@AttributeOverrides({ + @AttributeOverride(name = "version", column = @Column(name = "version", insertable = false, updatable = false)) +}) +public class i18n extends BaseEntity { + + @NotBlank + @Column + private String table_name; + @Column + private String field_name; + @Column + private Long record_id; + @Column + private String language; + @Column + private String value; + + public String getTable_name() { + return table_name; + } + public void setTable_name(String table_name) { + this.table_name = table_name; + } + public String getField_name() { + return field_name; + } + public void setField_name(String field_name) { + this.field_name = field_name; + } + public Long getRecord_id() { + return record_id; + } + public void setRecord_id(Long record_id) { + this.record_id = record_id; + } + public String getLanguage() { + return language; + } + public void setLanguage(String language) { + this.language = language; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18nRepository.java b/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18nRepository.java new file mode 100644 index 0000000..c1d2a06 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/I18n/enity/i18nRepository.java @@ -0,0 +1,8 @@ +package com.ffii.fhsmsc.modules.I18n.enity; + + +import com.ffii.core.support.AbstractRepository; + +public interface i18nRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/I18n/req/i18nReq.java b/src/main/java/com/ffii/fhsmsc/modules/I18n/req/i18nReq.java new file mode 100644 index 0000000..17175ca --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/I18n/req/i18nReq.java @@ -0,0 +1,71 @@ +package com.ffii.fhsmsc.modules.I18n.req; +import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonProperty; +public class i18nReq { + @JsonProperty("id") + private Long id; + @JsonProperty("table_name") + private String table_name; + @JsonProperty("field_name") + private String field_name; + @JsonProperty("record_id") + private Long record_id; + @JsonProperty("language") + private String language; + @JsonProperty("value") + private String value; + + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getTable_name() { + return table_name; + } + public void setTable_name(String table_name) { + this.table_name = table_name; + } + public String getField_name() { + return field_name; + } + public void setField_name(String field_name) { + this.field_name = field_name; + } + public Long getRecord_id() { + return record_id; + } + public void setRecord_id(Long record_id) { + this.record_id = record_id; + } + public String getLanguage() { + return language; + } + public void setLanguage(String language) { + this.language = language; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + +} + + + + + + + + + + + + + + + diff --git a/src/main/java/com/ffii/fhsmsc/modules/I18n/service/i18nService.java b/src/main/java/com/ffii/fhsmsc/modules/I18n/service/i18nService.java new file mode 100644 index 0000000..251f8e0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/I18n/service/i18nService.java @@ -0,0 +1,96 @@ +package com.ffii.fhsmsc.modules.I18n.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.I18n.enity.i18n; +import com.ffii.fhsmsc.modules.I18n.enity.i18nRepository; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; + +import jakarta.validation.Valid; + +@Service +public class i18nService extends AbstractBaseEntityService { + private static final Logger logger = LoggerFactory.getLogger(i18nService.class); + + public i18nService(JdbcDao jdbcDao, i18nRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `i18n` fi" + + " WHERE 1=1"); + if (args.containsKey("table_name")) { + sql.append(" AND fi.table_name = :table_name"); + } + if (args.containsKey("field_name")) { + sql.append(" AND fi.field_name = :field_name"); + } + if (args.containsKey("record_id")) { + sql.append(" AND fi.record_id = :record_id"); + } + if (args.containsKey("language")) { + sql.append(" AND fi.language = :language"); + } + sql.append(" ORDER BY fi.modified DESC"); // Ensure the most recent translation is first + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public i18n saveOrUpdate(@Valid i18nReq req) { + logger.info("Request: id={}, table_name={}, field_name={}, record_id={}, language={}, value={}", + req.getId(), req.getTable_name(), req.getField_name(), req.getRecord_id(), req.getLanguage(), req.getValue()); + + i18n instance; + + if (req.getId() != null) { + if (req.getId() > 0) { + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new i18n(); + } + } else { + instance = new i18n(); + } + + BeanUtils.copyProperties(req, instance); + logger.info("After BeanUtils: id={}, table_name={}, field_name={}, record_id={}, language={}, value={}", + instance.getId(), instance.getTable_name(), instance.getField_name(), instance.getRecord_id(), instance.getLanguage(), instance.getValue()); + + saveAndFlush(instance); + logger.info("After save: id={}, table_name={}, field_name={}, record_id={}, language={}, value={}", + instance.getId(), instance.getTable_name(), instance.getField_name(), instance.getRecord_id(), instance.getLanguage(), instance.getValue()); + + return instance; + } + + public void updateValue(String tableName, String fieldName, Long recordId, String language, String value) { + String sql = "UPDATE i18n SET value = :value WHERE table_name = :tableName AND field_name = :fieldName AND record_id = :recordId AND language = :language"; + Map params = new HashMap<>(); + params.put("value", value); + params.put("tableName", tableName); + params.put("fieldName", fieldName); + params.put("recordId", recordId); + params.put("language", language); + jdbcDao.executeUpdate(sql, params); + } + public void deleteByRecord(String tableName, Long recordId) { + String sql = "DELETE FROM i18n WHERE table_name = :tableName AND record_id = :recordId"; + Map params = new HashMap<>(); + params.put("tableName", tableName); + params.put("recordId", recordId); + jdbcDao.executeUpdate(sql, params); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/I18n/web/i18nController.java b/src/main/java/com/ffii/fhsmsc/modules/I18n/web/i18nController.java new file mode 100644 index 0000000..2df265b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/I18n/web/i18nController.java @@ -0,0 +1,56 @@ +package com.ffii.fhsmsc.modules.I18n.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/i18n") +public class i18nController { + private i18nService i18nService; + + public i18nController(i18nService i18nService) { + this.i18nService = i18nService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(i18nService.search( + CriteriaArgsBuilder.withRequest(request) + .addString("table_name") + .addString("field_name") + .addString("record_id") + .addString("language") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid i18nReq req) { + try { + //logger.info("Received save request: {}", req); + IdRes result = new IdRes(i18nService.saveOrUpdate(req).getId()); + //logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + //logger.error("Error processing save request", e); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group.java new file mode 100644 index 0000000..71b4d3f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "fb_group") +//@AttributeOverride(name = "id", column = @Column(name = "FB_group_id")) +//@AttributeOverride(name = "id", column = @Column(name = "FB_menu_id")) +public class fb_group extends BaseEntity { +@Column(name="id") +private Long id; + +@Column(name="FB_brand_id") +private Long FB_brand_id; + +@Column(name="FB_group_Name") +private String FB_group_Name; +public Long getId() { + return id; +} +public void setId(Long id) { + this.id = id; +} +public Long getFB_brand_id() { + return FB_brand_id; +} +public void setFB_brand_id(Long FB_brand_id) { + this.FB_brand_id = FB_brand_id; +} +public String getFB_group_Name() { + return FB_group_Name; +} +public void setFB_group_Name(String FB_group_Name) { + this.FB_group_Name = FB_group_Name; +} +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_groupResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_groupResponsity.java new file mode 100644 index 0000000..5f7a75f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_groupResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; + +import com.ffii.core.support.AbstractRepository; + +public interface fb_groupResponsity extends AbstractRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationship.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationship.java new file mode 100644 index 0000000..65381d7 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationship.java @@ -0,0 +1,47 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "fb_group_relationship") +//@AttributeOverride(name = "id", column = @Column(name = "FB_group_id")) +//@AttributeOverride(name = "id", column = @Column(name = "FB_menu_id")) +public class fb_group_relationship extends BaseEntity { +@Column(name="id") +private Long id; + +@Column(name="Fb_brand_id") +private Long Fb_brand_id; + +@Column(name="FB_group_id") +private Long FB_group_id; +@Column(name="FB_menu_id") +private Long FB_menu_id; +public Long getId () { + return id; +} +public void setId(Long id) { + this.id = id; +} +public Long getFb_brand_id() { + return Fb_brand_id; +} +public void setFb_brand_id(Long Fb_brand_id) { + this.Fb_brand_id = Fb_brand_id; +} +public Long getFB_group_id() { + return FB_group_id; +} +public void setFB_group_id(Long FB_group_id) { + this.FB_group_id = FB_group_id; +} +public Long getFB_menu_id() { + return FB_menu_id; +} +public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; +} +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationshipResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationshipResponsity.java new file mode 100644 index 0000000..f85f675 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_relationshipResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; + +import com.ffii.core.support.AbstractRepository; + +public interface fb_group_relationshipResponsity extends AbstractRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_control.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_control.java new file mode 100644 index 0000000..6b342a7 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_control.java @@ -0,0 +1,43 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "fb_group_show_control") +//@AttributeOverride(name = "id", column = @Column(name = "FB_group_id")) +//@AttributeOverride(name = "id", column = @Column(name = "FB_menu_id")) +public class fb_group_show_control extends BaseEntity { +@Column(name="id") +private Long id; + +@Column(name="Fb_brand_id") +private Long Fb_brand_id; + +@Column(name="FB_group_id") +private Long FB_group_id; +@Column(name="show_table") +private Boolean show_table; + +public Long getFb_brand_id() { + return Fb_brand_id; +} +public void setFb_brand_id(Long Fb_brand_id) { + this.Fb_brand_id = Fb_brand_id; +} +public Long getFB_group_id() { + return FB_group_id; +} +public void setFB_group_id(Long FB_group_id) { + this.FB_group_id = FB_group_id; +} + +public Boolean getShow_table() { + return show_table; +} +public void setShow_table(Boolean show_table) { + this.show_table = show_table; +} +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_controlResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_controlResponsity.java new file mode 100644 index 0000000..05ed6f4 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_group_show_controlResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; + +import com.ffii.core.support.AbstractRepository; + +public interface fb_group_show_controlResponsity extends AbstractRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu.java new file mode 100644 index 0000000..67fa639 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu.java @@ -0,0 +1,94 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonProperty; +/** @author Terence */ +@Entity +@Table(name = "fb_menu") + +public class fb_menu extends BaseEntity { + + @Column(name = "id") + private Long id; + + @Column + @JsonProperty("Menu_Item_Description") + private String Menu_Item_Description; + @Column + @JsonProperty("Category") + private String category; + @Column + @JsonProperty("Price") + private Double price; + @Column + @JsonProperty("Is_Vegan") + private Boolean is_vegan; + @Column + @JsonProperty("Is_Vegetarian") + private Boolean is_vegetarian; + @Column + @JsonProperty("Is_Gluten_Free") + private Boolean is_gluten_free; + @Column + @JsonProperty("Is_Spicy") + private Boolean is_spicy; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getMenu_Item_Description() { + return Menu_Item_Description; + } + public void setMenu_Item_Description(String Menu_Item_Description) { + this.Menu_Item_Description = Menu_Item_Description; + } + public String getCategory() { + return category; + } + public void setCategory(String category) { + this.category = category; + } + public Double getPrice() { + return price; + } + public void setPrice(Double price) { + this.price = price; + } + public Boolean getIs_vegan() { + return is_vegan; + } + public void setIs_vegan(Boolean is_vegan) { + this.is_vegan = is_vegan; + } + public Boolean getIs_vegetarian() { + return is_vegetarian; + } + public void setIs_vegetarian(Boolean is_vegetarian) { + this.is_vegetarian = is_vegetarian; + } + public Boolean getIs_gluten_free() { + return is_gluten_free; + } + public void setIs_gluten_free(Boolean is_gluten_free) { + this.is_gluten_free = is_gluten_free; + } + public Boolean getIs_spicy() { + return is_spicy; + } + public void setIs_spicy(Boolean is_spicy) { + this.is_spicy = is_spicy; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menuResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menuResponsity.java new file mode 100644 index 0000000..3a95ae0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menuResponsity.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.support.AbstractRepository; + +public interface fb_menuResponsity extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationship.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationship.java new file mode 100644 index 0000000..ece43b8 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationship.java @@ -0,0 +1,51 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import com.ffii.core.entity.BaseEntity; + +/** @author Terence */ +@Entity +@Table(name = "fb_menu_and_outlet_relationship") +public class fb_menu_and_outlet_relationship extends BaseEntity { + @Column + private Long id; + @Column + private Long FB_id; + @Column + private Long FB_outlet_id; + @Column + private Long FB_menu_id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getFB_id() { + return FB_id; + } + + public void setFB_id(Long FB_id) { + this.FB_id = FB_id; + } + + public Long getFB_outlet_id() { + return FB_outlet_id; + } + + public void setFB_outlet_id(Long FB_outlet_id) { + this.FB_outlet_id = FB_outlet_id; + } + + public Long getFB_menu_id() { + return FB_menu_id; + } + + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationshipResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationshipResponsity.java new file mode 100644 index 0000000..eac9d7b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_and_outlet_relationshipResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.support.AbstractRepository; + +public interface fb_menu_and_outlet_relationshipResponsity extends AbstractRepository { + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredient.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredient.java new file mode 100644 index 0000000..f66faea --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredient.java @@ -0,0 +1,59 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "fb_menu_item_ingredient") +public class fb_menu_item_ingredient extends BaseEntity { + @Column + private Long id; + @Column + private Long FB_menu_id; + @Column + private Long Ingredient_id; + @Column + private Long Ingredient_volume; + @Column + private String Ingredient_volume_unit; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getFB_menu_id() { + return FB_menu_id; + } + + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + + public Long getIngredient_id() { + return Ingredient_id; + } + + public Long getIngredient_volume() { + return Ingredient_volume; + } + + public void setIngredient_volume(Long Ingredient_volume) { + this.Ingredient_volume = Ingredient_volume; + } + + public String getIngredient_volume_unit() { + return Ingredient_volume_unit; + } + + public void setIngredient_volume_unit(String Ingredient_volume_unit) { + this.Ingredient_volume_unit = Ingredient_volume_unit; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredientResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredientResponsity.java new file mode 100644 index 0000000..3c9735e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_ingredientResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; + +import com.ffii.core.support.AbstractRepository; + +public interface fb_menu_item_ingredientResponsity extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutrition.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutrition.java new file mode 100644 index 0000000..388587a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutrition.java @@ -0,0 +1,185 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import com.ffii.core.entity.BaseEntity; +import java.time.LocalDateTime; +/** @author Terence */ +@Entity +@Table(name = "fb_menu_item_nutrition") +public class fb_menu_item_nutrition extends BaseEntity { + @Column + private Long id; + + @Column(name = "FB_menu_id") + private Long FB_menu_id; + + @Column(name = "Calories") + private BigDecimal Calories; + + @Column(name = "Protein") + private BigDecimal Protein; + + @Column(name = "Fat") + private BigDecimal Fat; + + @Column(name = "Carbohydrate") + private BigDecimal Carbohydrate; + + @Column(name = "Sodium") + private BigDecimal Sodium; + + @Column(name = "Calcium") + private BigDecimal Calcium; + + @Column(name = "Phosphorus") + private BigDecimal Phosphorus; + + @Column(name = "Iron") + private BigDecimal Iron; + + @Column(name = "Potassium") + private BigDecimal Potassium; + + @Column(name = "VitaminA") + private BigDecimal VitaminA; + + @Column(name = "VitaminB1") + private BigDecimal VitaminB1; + + @Column(name = "VitaminB2") + private BigDecimal VitaminB2; + + @Column(name = "Niacin") + private BigDecimal Niacin; + + @Column(name = "VitaminC") + private BigDecimal VitaminC; + + @Column(name = "Nutrient_Volume") + private BigDecimal Nutrient_Volume; + + @Column(name = "Nutrient_Volume_Unit") + private String Nutrient_Volume_Unit; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getFB_menu_id() { + return FB_menu_id; + } + + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + + public BigDecimal getCalories() { + return Calories; + } + + public void setCalories(BigDecimal Calories) { + this.Calories = Calories; + } + + public BigDecimal getProtein() { + return Protein; + } + + public void setProtein(BigDecimal Protein) { + this.Protein = Protein; + } + + public BigDecimal getCarbs() { + return Carbohydrate; + } + + public void setCarbs(BigDecimal Carbohydrate) { + this.Carbohydrate = Carbohydrate; + } + + public BigDecimal getFat() { + return Fat; + } + + public void setFat(BigDecimal Fat) { + this.Fat = Fat; + } + + public BigDecimal getSodium() { + return Sodium; + } + + public void setSodium(BigDecimal Sodium) { + this.Sodium = Sodium; + } + + public BigDecimal getCalcium() { + return Calcium; + } + + public void setCalcium(BigDecimal Calcium) { + this.Calcium = Calcium; + } + + public BigDecimal getIron() { + return Iron; + } + + public void setIron(BigDecimal Iron) { + this.Iron = Iron; + } + + public BigDecimal getPotassium() { + return Potassium; + } + + public void setPotassium(BigDecimal Potassium) { + this.Potassium = Potassium; + } + + public BigDecimal getVitaminA() { + return VitaminA; + } + + public void setVitaminA(BigDecimal VitaminA) { + this.VitaminA = VitaminA; + } + + public BigDecimal getVitaminB1() { + return VitaminB1; + } + + public void setVitaminB1(BigDecimal VitaminB1) { + this.VitaminB1 = VitaminB1; + } + + public BigDecimal getVitaminB2() { + return VitaminB2; + } + + public void setVitaminB2(BigDecimal VitaminB2) { + this.VitaminB2 = VitaminB2; + } + + public BigDecimal getNiacin() { + return Niacin; + } + + public void setNiacin(BigDecimal Niacin) { + this.Niacin = Niacin; + } + + public BigDecimal getVitaminC() { + return VitaminC; + } + + public void setVitaminC(BigDecimal VitaminC) { + this.VitaminC = VitaminC; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutritionResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutritionResponsity.java new file mode 100644 index 0000000..710a739 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/fb_menu_item_nutritionResponsity.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.support.AbstractRepository; + +public interface fb_menu_item_nutritionResponsity extends AbstractRepository { + +} + \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredient.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredient.java new file mode 100644 index 0000000..4102a97 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredient.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "ingredient") +@AttributeOverride(name = "id", column = @Column(name = "Ingredient_id")) +//@AttributeOverride(name = "id", column = @Column(name = "FB_menu_id")) +public class ingredient extends BaseEntity { + + @Column(name = "Ingredient_id", insertable = false, updatable = false) + private Long Ingredient_id; + + @Column(name = "Ingredient_Name") + private String Ingredient_Name; + + @Column(name = "Allergy_Substance_ID") + private Long Allergy_Substance_ID; + + public void setIngredient_id(Long Ingredient_id) { + this.Ingredient_id = Ingredient_id; + } + public Long getIngredient_id() { + return Ingredient_id; + } + + public void setIngredient_Name(String Ingredient_Name) { + this.Ingredient_Name = Ingredient_Name; + } + public String getIngredient_Name() { + return Ingredient_Name; + } + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredientResponsity.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredientResponsity.java new file mode 100644 index 0000000..6911502 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/enity/ingredientResponsity.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.Menu.enity; +import com.ffii.core.support.AbstractRepository; + +public interface ingredientResponsity extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ new file mode 100644 index 0000000..6c50f02 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/.LCKfb_menuResponsity.java~ @@ -0,0 +1 @@ +C:\workspace\FHSMS-C-backend\src\main\java\com\ffii\fhsmsc\modules\Menu\req\fb_menuResponsity.java \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/FbMenuItemNutrientReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/FbMenuItemNutrientReq.java new file mode 100644 index 0000000..aea63b9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/FbMenuItemNutrientReq.java @@ -0,0 +1,139 @@ +package com.ffii.fhsmsc.modules.Menu.req; +import java.math.BigDecimal; +import java.time.LocalDateTime; +public class FbMenuItemNutrientReq { + private Long id; + private Long FB_menu_id; + private BigDecimal Calories; + private BigDecimal Protein; + private BigDecimal Fat; + private BigDecimal Carbohydrate; + private BigDecimal Sodium; + private BigDecimal Calcium; + private BigDecimal Phosphorus; + private BigDecimal Iron; + private BigDecimal Potassium; + private BigDecimal VitaminA; + private BigDecimal VitaminB1; + private BigDecimal VitaminB2; + private BigDecimal Niacin; + private BigDecimal VitaminC; + private BigDecimal Nutrient_Volume; + private String Nutrient_Volume_Unit; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_menu_id() { + return FB_menu_id; + } + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + public BigDecimal getCalories() { + return Calories; + } + public void setCalories(BigDecimal Calories) { + this.Calories = Calories; + } + public BigDecimal getProtein() { + return Protein; + } + public void setProtein(BigDecimal Protein) { + this.Protein = Protein; + } + public BigDecimal getFat() { + return Fat; + } + public void setFat(BigDecimal Fat) { + this.Fat = Fat; + } + public BigDecimal getCarbohydrate() { + return Carbohydrate; + } + public void setCarbohydrate(BigDecimal Carbohydrate) { + this.Carbohydrate = Carbohydrate; + } + public BigDecimal getSodium() { + return Sodium; + } + public void setSodium(BigDecimal Sodium) { + this.Sodium = Sodium; + } + public BigDecimal getCalcium() { + return Calcium; + } + public void setCalcium(BigDecimal Calcium) { + this.Calcium = Calcium; + } + public BigDecimal getPhosphorus() { + return Phosphorus; + } + public void setPhosphorus(BigDecimal Phosphorus) { + this.Phosphorus = Phosphorus; + } + public BigDecimal getIron() { + return Iron; + } + public void setIron(BigDecimal Iron) { + this.Iron = Iron; + } + public BigDecimal getPotassium() { + return Potassium; + } + public void setPotassium(BigDecimal Potassium) { + this.Potassium = Potassium; + } + public BigDecimal getVitaminA() { + return VitaminA; + } + public void setVitaminA(BigDecimal VitaminA) { + this.VitaminA = VitaminA; + } + public BigDecimal getVitaminB1() { + return VitaminB1; + } + public void setVitaminB1(BigDecimal VitaminB1) { + this.VitaminB1 = VitaminB1; + } + public BigDecimal getVitaminB2() { + return VitaminB2; + } + public void setVitaminB2(BigDecimal VitaminB2) { + this.VitaminB2 = VitaminB2; + } + public BigDecimal getNiacin() { + return Niacin; + } + public void setNiacin(BigDecimal Niacin) { + this.Niacin = Niacin; + } + public BigDecimal getVitaminC() { + return VitaminC; + } + public void setVitaminC(BigDecimal VitaminC) { + this.VitaminC = VitaminC; + } + public BigDecimal getNutrient_Volume() { + return Nutrient_Volume; + } + public void setNutrient_Volume(BigDecimal Nutrient_Volume) { + this.Nutrient_Volume = Nutrient_Volume; + } + public String getNutrient_Volume_Unit() { + return Nutrient_Volume_Unit; + } + public void setNutrient_Volume_Unit(String Nutrient_Volume_Unit) { + this.Nutrient_Volume_Unit = Nutrient_Volume_Unit; + } + public LocalDateTime getCreatedAt() { + return createdAt; + } + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_groupReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_groupReq.java new file mode 100644 index 0000000..bb82f7e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_groupReq.java @@ -0,0 +1,50 @@ +package com.ffii.fhsmsc.modules.Menu.req; +import com.fasterxml.jackson.annotation.JsonProperty; +public class fb_groupReq { + @JsonProperty("id") + private Long id; + @JsonProperty("FB_brand_id") + private Long FB_brand_id; + @JsonProperty("FB_group_Name") + private String FB_group_Name; + @JsonProperty("FB_group_Name_en") + private String FB_group_Name_en; + @JsonProperty("FB_group_Name_zh") + private String FB_group_Name_zh; + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_brand_id() { + return FB_brand_id; + } + public void setFB_brand_id(Long FB_brand_id) { + this.FB_brand_id = FB_brand_id; + } + public String getFB_group_Name() { + return FB_group_Name; + } + public void setFB_group_Name(String FB_group_Name) { + this.FB_group_Name = FB_group_Name; + } + public String getFB_group_Name_en() { + return FB_group_Name_en; + } + public void setFB_group_Name_en(String FB_group_Name_en) { + this.FB_group_Name_en = FB_group_Name_en; + } + public String getFB_group_Name_zh() { + return FB_group_Name_zh; + } + public void setFB_group_Name_zh(String FB_group_Name_zh) { + this.FB_group_Name_zh = FB_group_Name_zh; + } + + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_relationshipReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_relationshipReq.java new file mode 100644 index 0000000..f8829c1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_relationshipReq.java @@ -0,0 +1,44 @@ +package com.ffii.fhsmsc.modules.Menu.req; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class fb_group_relationshipReq { + private Long id; + @JsonProperty("Fb_brand_id") + private Long Fb_brand_id; + @JsonProperty("FB_group_id") + private Long FB_group_id; + @JsonProperty("FB_menu_id") + private Long FB_menu_id; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFb_brand_id() { + return Fb_brand_id; + } + public void setFb_brand_id(Long Fb_brand_id) { + this.Fb_brand_id = Fb_brand_id; + } + public Long getFB_group_id() { + return FB_group_id; + } + public void setFB_group_id(Long FB_group_id) { + this.FB_group_id = FB_group_id; + } + public Long getFB_menu_id() { + return FB_menu_id; + } + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_show_controlReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_show_controlReq.java new file mode 100644 index 0000000..846df64 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_group_show_controlReq.java @@ -0,0 +1,42 @@ +package com.ffii.fhsmsc.modules.Menu.req; +import com.fasterxml.jackson.annotation.JsonProperty; +public class fb_group_show_controlReq { + private Long id; + @JsonProperty("FB_group_id") + private Long FB_group_id; + @JsonProperty("FB_brand_id") + private Long FB_brand_id; + @JsonProperty("show_table") + private Boolean show_table; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_group_id() { + return FB_group_id; + } + public void setFB_group_id(Long FB_group_id) { + this.FB_group_id = FB_group_id; + } + public Long getFB_brand_id() { + return FB_brand_id; + } + public void setFB_brand_id(Long FB_brand_id) { + this.FB_brand_id = FB_brand_id; + } + public Boolean getShow_table() { + return show_table; + } + public void setShow_table(Boolean show_table) { + this.show_table = show_table; + } + + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menuReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menuReq.java new file mode 100644 index 0000000..1d205dc --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menuReq.java @@ -0,0 +1,106 @@ +package com.ffii.fhsmsc.modules.Menu.req; + +import com.fasterxml.jackson.annotation.JsonProperty; +public class fb_menuReq { + @JsonProperty("id") + private Long id; + @JsonProperty("Menu_Item_Description") + private String Menu_Item_Description; + @JsonProperty("category") + private String category; + @JsonProperty("category_en") + private String category_en; + @JsonProperty("category_zh") + private String category_zh; + @JsonProperty("price") + private Double price; + @JsonProperty("is_vegan") + private Boolean is_vegan; + @JsonProperty("is_vegetarian") + private Boolean is_vegetarian; + @JsonProperty("is_gluten_free") + private Boolean is_gluten_free; + @JsonProperty("is_spicy") + private Boolean is_spicy; + @JsonProperty("Menu_Item_Description_en") + private String Menu_Item_Description_en; + @JsonProperty("Menu_Item_Description_zh") + private String Menu_Item_Description_zh; + @JsonProperty("userId") + private Long userId; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getMenu_Item_Description() { + return Menu_Item_Description; + } + public void setMenu_Item_Description(String Menu_Item_Description) { + this.Menu_Item_Description = Menu_Item_Description; + } + public String getCategory() { + return category; + } + public void setCategory(String category) { + this.category = category; + } + public String getCategory_en() { + return category_en; + } + public void setCategory_en(String category_en) { + this.category_en = category_en; + } + public String getCategory_zh() { + return category_zh; + } + public void setCategory_zh(String category_zh) { + this.category_zh = category_zh; + } + public Double getPrice() { + return price; + } + public void setPrice(Double price) { + this.price = price; + } + public Boolean getIs_vegan() { + return is_vegan; + } + public void setIs_vegan(Boolean is_vegan) { + this.is_vegan = is_vegan; + } + public Boolean getIs_vegetarian() { + return is_vegetarian; + } + public void setIs_vegetarian(Boolean is_vegetarian) { + this.is_vegetarian = is_vegetarian; + } + public Boolean getIs_gluten_free() { + return is_gluten_free; + } + public void setIs_gluten_free(Boolean is_gluten_free) { + this.is_gluten_free = is_gluten_free; + } + public Boolean getIs_spicy() { + return is_spicy; + } + public void setIs_spicy(Boolean is_spicy) { + this.is_spicy = is_spicy; + } + public String getMenu_Item_Description_en() { + return Menu_Item_Description_en; + } + public void setMenu_Item_Description_en(String Menu_Item_Description_en) { + this.Menu_Item_Description_en = Menu_Item_Description_en; + } + public String getMenu_Item_Description_zh() { + return Menu_Item_Description_zh; + } + public void setMenu_Item_Description_zh(String Menu_Item_Description_zh) { + this.Menu_Item_Description_zh = Menu_Item_Description_zh; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_and_outlet_relationshipReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_and_outlet_relationshipReq.java new file mode 100644 index 0000000..f47644a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_and_outlet_relationshipReq.java @@ -0,0 +1,32 @@ +package com.ffii.fhsmsc.modules.Menu.req; + +public class fb_menu_and_outlet_relationshipReq { + private Long id; + private Long FB_menu_id; + private Long Outlet_id; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_menu_id() { + return FB_menu_id; + } + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + public Long getOutlet_id() { + return Outlet_id; + } + public void setOutlet_id(Long Outlet_id) { + this.Outlet_id = Outlet_id; + } + + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_item_ingredientReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_item_ingredientReq.java new file mode 100644 index 0000000..b6e406c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/fb_menu_item_ingredientReq.java @@ -0,0 +1,47 @@ +package com.ffii.fhsmsc.modules.Menu.req; +import java.math.BigDecimal; +public class fb_menu_item_ingredientReq { + private Long id; + private Long FB_menu_id; + private Long Ingredient_id; + private BigDecimal Ingredient_Volume; + private String Ingredient_Volume_Unit; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public Long getFB_menu_id() { + return FB_menu_id; + } + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + public Long getIngredient_id() { + return Ingredient_id; + } + public void setIngredient_id(Long Ingredient_id) { + this.Ingredient_id = Ingredient_id; + } + public BigDecimal getIngredient_Volume() { + return Ingredient_Volume; + } + public void setIngredient_Volume(BigDecimal Ingredient_Volume) { + this.Ingredient_Volume = Ingredient_Volume; + } + public String getIngredient_Volume_Unit() { + return Ingredient_Volume_Unit; + } + public void setIngredient_Volume_Unit(String Ingredient_Volume_Unit) { + this.Ingredient_Volume_Unit = Ingredient_Volume_Unit; + } + + + + + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/req/ingredientReq.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/ingredientReq.java new file mode 100644 index 0000000..43b8309 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/req/ingredientReq.java @@ -0,0 +1,47 @@ +package com.ffii.fhsmsc.modules.Menu.req; + +import com.fasterxml.jackson.annotation.JsonProperty; +public class ingredientReq { + @JsonProperty("Ingredient_id") + private Long Ingredient_id; + @JsonProperty("Ingredient_Name") + private String Ingredient_Name; + @JsonProperty("Ingredient_Name_en") + private String Ingredient_Name_en; + @JsonProperty("Ingredient_Name_zh") + private String Ingredient_Name_zh; + @JsonProperty("Allergy_Substance_ID") + private Long Allergy_Substance_ID; + public Long getIngredient_id() { + return Ingredient_id; + } + public void setIngredient_id(Long Ingredient_id) { + this.Ingredient_id = Ingredient_id; + } + public String getIngredient_Name() { + return Ingredient_Name; + } + public void setIngredient_Name(String Ingredient_Name) { + this.Ingredient_Name = Ingredient_Name; + } + public String getIngredient_Name_en() { + return Ingredient_Name_en; + } + public void setIngredient_Name_en(String Ingredient_Name_en) { + this.Ingredient_Name_en = Ingredient_Name_en; + } + public String getIngredient_Name_zh() { + return Ingredient_Name_zh; + } + public void setIngredient_Name_zh(String Ingredient_Name_zh) { + this.Ingredient_Name_zh = Ingredient_Name_zh; + } + public Long getAllergy_Substance_ID() { + return Allergy_Substance_ID; + } + public void setAllergy_Substance_ID(Long Allergy_Substance_ID) { + this.Allergy_Substance_ID = Allergy_Substance_ID; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/FbMenuCombineService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/FbMenuCombineService.java new file mode 100644 index 0000000..5490252 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/FbMenuCombineService.java @@ -0,0 +1,1225 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import com.ffii.fhsmsc.modules.Menu.req.fb_menu_item_ingredientReq; +@Service +public class FbMenuCombineService { + + private final JdbcDao jdbcDao; + private final fb_menuService menuService; + private final fb_menu_item_nutritionService nutritionService; + private final fb_menu_item_ingredientService ingredientService; + private final fb_menu_and_outlet_relationshipService relationshipService; + private final i18nService i18nService; + private static final Logger logger = LoggerFactory.getLogger(FbMenuCombineService.class); + @Autowired + public FbMenuCombineService( + JdbcDao jdbcDao, + fb_menuService menuService, + fb_menu_item_nutritionService nutritionService, + fb_menu_item_ingredientService ingredientService, + fb_menu_and_outlet_relationshipService relationshipService, + i18nService i18nService) { + this.jdbcDao = jdbcDao; + this.menuService = menuService; + this.nutritionService = nutritionService; + this.ingredientService = ingredientService; + this.relationshipService = relationshipService; + this.i18nService = i18nService; + } + + /** + * 获取完整的菜单信息,包括营养和配料 + */ + public Map getCompleteMenu(Long menuId) { + Map result = new HashMap<>(); + Map params = new HashMap<>(); + params.put("menuId", menuId); + + // 主表查询 + StringBuilder sql = new StringBuilder( + "SELECT m.*, " + + "i_en.value as en_description, " + + "i_zh.value as zh_description " + + "FROM fb_menu m " + + "LEFT JOIN i18n i_en ON m.id = i_en.record_id " + + "AND i_en.table_name = 'fb_menu' " + + "AND i_en.field_name = 'Menu_Item_Description' " + + "AND i_en.language = 'en' " + + "LEFT JOIN i18n i_zh ON m.id = i_zh.record_id " + + "AND i_zh.table_name = 'fb_menu' " + + "AND i_zh.field_name = 'Menu_Item_Description' " + + "AND i_zh.language = 'zh' " + + "WHERE m.id = :menuId"); // 这里改了 + + List> menuInfo = jdbcDao.queryForList(sql.toString(), params); + + if (menuInfo.isEmpty()) { + return null; + } + + // 基本菜单信息 + result.put("menu", menuInfo.get(0)); + + // 获取营养信息 + StringBuilder nutritionSql = new StringBuilder( + "SELECT * FROM fb_menu_item_nutrition WHERE FB_menu_id = :menuId"); // 这里改了 + List> nutritionData = jdbcDao.queryForList(nutritionSql.toString(), params); + result.put("nutritions", nutritionData); + + // 获取配料信息 + StringBuilder ingredientSql = new StringBuilder( + "SELECT i.*, ing.Ingredient_Name " + + "FROM fb_menu_item_ingredient i " + + "LEFT JOIN Ingredient ing ON i.ingredient_id = ing.Ingredient_id " + + "WHERE i.FB_menu_id = :menuId"); // 这里改了 + List> ingredientData = jdbcDao.queryForList(ingredientSql.toString(), params); + result.put("ingredients", ingredientData); + + return result; + } + /** + * 获取特定门店的所有菜单 + */ + public List> getOutletMenus(Long outletId) { + List> result = new ArrayList<>(); + + // 查询该门店关联的所有菜单ID + StringBuilder sql = new StringBuilder( + "SELECT FB_menu_id FROM fb_menu_and_outlet_relationship " + + "WHERE FB_outlet_id = :outletId"); + + Map args = new HashMap<>(); + args.put("outletId", outletId); + List> menuIds = jdbcDao.queryForList(sql.toString(), args); + + // 为每个菜单获取完整信息 + for (Map item : menuIds) { + Long menuId = ((Number) item.get("FB_menu_id")).longValue(); + Map completeMenu = getCompleteMenu(menuId); + if (completeMenu != null) { + result.add(completeMenu); + } + } + + return result; + } + + /** + * 一次性查询获取多个菜单的完整信息(更高效) + */ + public List> getBatchMenus(List menuIds) { + if (menuIds == null || menuIds.isEmpty()) { + return new ArrayList<>(); + } + + List> result = new ArrayList<>(); + + // 一次性查询所有菜单基本信息 + StringBuilder menuSql = new StringBuilder( + "SELECT * FROM fb_menu WHERE id IN (:menuIds)"); + Map args = new HashMap<>(); + args.put("menuIds", menuIds); + List> menus = menuService.search(args); + + // 一次性查询所有营养信息 + StringBuilder nutritionSql = new StringBuilder( + "SELECT * FROM fb_menu_item_nutrition WHERE FB_menu_id IN (:menuIds)"); + List> allNutritions = nutritionService.search(args); + + // 一次性查询所有配料信息 + StringBuilder ingredientSql = new StringBuilder( + "SELECT i.*, ing.Ingredient_Name " + + "FROM fb_menu_item_ingredient i " + + "LEFT JOIN Ingredient ing ON i.ingredient_id = ing.Ingredient_id " + + "WHERE i.FB_menu_id IN (:menuIds)"); + List> allIngredients = ingredientService.search(args); + + // 为每个菜单组装数据 + for (Map menu : menus) { + Long menuId = ((Number) menu.get("FB_menu_id")).longValue(); + Map completeMenu = new HashMap<>(); + completeMenu.put("menu", menu); + + // 过滤出当前菜单的营养信息 + List> menuNutritions = allNutritions.stream() + .filter(n -> menuId.equals(((Number) n.get("FB_menu_id")).longValue())) + .toList(); + completeMenu.put("nutritions", menuNutritions); + + // 过滤出当前菜单的配料信息 + List> menuIngredients = allIngredients.stream() + .filter(i -> menuId.equals(((Number) i.get("FB_menu_id")).longValue())) + .toList(); + completeMenu.put("ingredients", menuIngredients); + + result.add(completeMenu); + } + + return result; + } + + /** + * 保存完整的菜单信息,包括营养、配料和国际化信息 + */ + public void saveCompleteMenu(Map menuData) { + // 1. 保存菜单基本信息 + Map menu = (Map) menuData.get("menu"); + Long menuId = menu.get("FB_menu_id") != null ? ((Number) menu.get("FB_menu_id")).longValue() : null; + Long fbId = ((Number) menu.get("FB_id")).longValue(); + + // 检查菜单是否已存在 + StringBuilder checkSql = new StringBuilder( + "SELECT COUNT(*) FROM fb_menu WHERE FB_menu_id = :menuId"); + Map checkParams = new HashMap<>(); + checkParams.put("menuId", menuId); + int count = menuId != null ? jdbcDao.queryForInt(checkSql.toString(), checkParams) : 0; + + // 如果menuId为空,生成一个新的ID + if (menuId == null) { + StringBuilder maxIdSql = new StringBuilder("SELECT COALESCE(MAX(FB_menu_id), 0) AS max_id FROM fb_menu"); + List> result = jdbcDao.queryForList(maxIdSql.toString(), new HashMap<>()); + Long maxId = 0L; + if (!result.isEmpty()) { + Object maxIdObj = result.get(0).get("max_id"); + maxId = maxIdObj != null ? ((Number) maxIdObj).longValue() : 0L; + } + menuId = maxId + 1; + menu.put("FB_menu_id", menuId); + } + + // 根据是否存在决定是插入还是更新 + StringBuilder menuSql; + if (count == 0) { + menuSql = new StringBuilder( + "INSERT INTO fb_menu (FB_id, FB_menu_id, Menu_Item_Description) " + + "VALUES (:fbId, :menuId, :description)"); + } else { + menuSql = new StringBuilder( + "UPDATE fb_menu SET FB_id = :fbId, Menu_Item_Description = :description " + + "WHERE FB_menu_id = :menuId"); + } + + Map menuParams = new HashMap<>(); + menuParams.put("fbId", fbId); + menuParams.put("menuId", menuId); + menuParams.put("description", menu.get("Menu_Item_Description")); + jdbcDao.executeUpdate(menuSql.toString(), menuParams); + + // 2. 保存国际化信息 + i18nReq enReq = new i18nReq(); + enReq.setTable_name("fb_menu"); + enReq.setRecord_id(menuId); + enReq.setField_name("Menu_Item_Description"); + enReq.setLanguage("en"); + enReq.setValue((String) menu.get("en_description")); + i18nService.saveOrUpdate(enReq); + + i18nReq zhReq = new i18nReq(); + zhReq.setTable_name("fb_menu"); + zhReq.setRecord_id(menuId); + zhReq.setField_name("Menu_Item_Description"); + zhReq.setLanguage("zh"); + zhReq.setValue((String) menu.get("zh_description")); + i18nService.saveOrUpdate(zhReq); + + // 3. 保存配料信息 + StringBuilder deleteIngredientsSql = new StringBuilder( + "DELETE FROM fb_menu_item_ingredient WHERE FB_menu_id = :menuId"); + jdbcDao.executeUpdate(deleteIngredientsSql.toString(), checkParams); + + List> ingredients = (List>) menuData.get("ingredients"); + if (ingredients != null) { + for (Map ingredient : ingredients) { + Long ingredientId = ingredient.get("Ingredient_id") != null ? ((Number) ingredient.get("Ingredient_id")).longValue() : null; + + // Check if Ingredient_id is missing or out of range for a 32-bit INT + if (ingredientId == null || ingredientId > Integer.MAX_VALUE || ingredientId < Integer.MIN_VALUE) { + // Generate a new ingredient ID + StringBuilder maxIngredientIdSql = new StringBuilder("SELECT COALESCE(MAX(Ingredient_id), 0) AS max_id FROM Ingredient"); + List> ingredientResult = jdbcDao.queryForList(maxIngredientIdSql.toString(), new HashMap<>()); + Long maxIngredientId = 0L; + if (!ingredientResult.isEmpty()) { + Object maxIdObj = ingredientResult.get(0).get("max_id"); + maxIngredientId = maxIdObj != null ? ((Number) maxIdObj).longValue() : 0L; + } + ingredientId = maxIngredientId + 1; + + // Insert new ingredient into Ingredient table + StringBuilder insertIngredientSql = new StringBuilder( + "INSERT INTO Ingredient (Ingredient_id, Ingredient_Name) VALUES (:id, :name)"); + Map ingredientParams = new HashMap<>(); + ingredientParams.put("id", ingredientId); + ingredientParams.put("name", ingredient.get("Ingredient_Name")); + jdbcDao.executeUpdate(insertIngredientSql.toString(), ingredientParams); + } + + fb_menu_item_ingredientReq ingredientReq = new fb_menu_item_ingredientReq(); + if (menuData.get("FB_menu_id") != null) { + ingredientReq.setId(menuId); // 设置ID以更新现有记录 + } + ingredientReq.setFB_menu_id(menuId); + ingredientReq.setIngredient_id(((Number) ingredient.get("Ingredient_id")).longValue()); + ingredientReq.setIngredient_Volume(ingredient.get("Ingredient_Volume") != null ? + new BigDecimal(ingredient.get("Ingredient_Volume").toString()) : null); + ingredientReq.setIngredient_Volume_Unit((String) ingredient.get("Ingredient_Volume_Unit")); + /* + if (ingredient.get("Allergy_Substance_ID") != null) { + ingredientReq.setAllergy_Substance_ID(Long.valueOf(((Number) ingredient.get("Allergy_Substance_ID")).longValue())); + } + */ + ingredientService.saveOrUpdate(ingredientReq); + + // 保存配料名称的国际化信息 + i18nReq enIngredientReq = new i18nReq(); + enIngredientReq.setTable_name("Ingredient"); + enIngredientReq.setRecord_id(ingredientId); + enIngredientReq.setField_name("Ingredient_Name"); + enIngredientReq.setLanguage("en"); + enIngredientReq.setValue((String) ingredient.get("en_Ingredient_Name")); + i18nService.saveOrUpdate(enIngredientReq); + + i18nReq zhIngredientReq = new i18nReq(); + zhIngredientReq.setTable_name("Ingredient"); + zhIngredientReq.setRecord_id(ingredientId); + zhIngredientReq.setField_name("Ingredient_Name"); + zhIngredientReq.setLanguage("zh"); + zhIngredientReq.setValue((String) ingredient.get("zh_Ingredient_Name")); + i18nService.saveOrUpdate(zhIngredientReq); + } + } + + // 4. 保存营养信息 + StringBuilder deleteNutritionSql = new StringBuilder( + "DELETE FROM fb_menu_item_nutrition WHERE FB_menu_id = :menuId"); + jdbcDao.executeUpdate(deleteNutritionSql.toString(), checkParams); + + List> nutritions = (List>) menuData.get("nutritions"); + if (nutritions != null) { + for (Map nutrition : nutritions) { + StringBuilder nutritionSql = new StringBuilder( + "INSERT INTO fb_menu_item_nutrition " + + "(FB_menu_id, Calories, Protein, Fat, Carbohydrate, Sodium, Potassium, " + + "Calcium, Phosphorus, Iron, VitaminA, VitaminB1, VitaminB2, Niacin, " + + "VitaminC, Nutrient_Volume, Nutrient_Volume_Unit) " + + "VALUES (:menuId, :calories, :protein, :fat, :carbohydrate, :sodium, " + + ":potassium, :calcium, :phosphorus, :iron, :vitaminA, :vitaminB1, " + + ":vitaminB2, :niacin, :vitaminC, :volume, :unit)"); + + Map nutritionParams = new HashMap<>(); + nutritionParams.put("menuId", menuId); + nutritionParams.put("calories", nutrition.get("Calories")); + nutritionParams.put("protein", nutrition.get("Protein")); + nutritionParams.put("fat", nutrition.get("Fat")); + nutritionParams.put("carbohydrate", nutrition.get("Carbohydrate")); + nutritionParams.put("sodium", nutrition.get("Sodium")); + nutritionParams.put("potassium", nutrition.get("Potassium")); + nutritionParams.put("calcium", nutrition.get("Calcium")); + nutritionParams.put("phosphorus", nutrition.get("Phosphorus") != null ? nutrition.get("Phosphorus") : 0.0); + nutritionParams.put("iron", nutrition.get("Iron") != null ? nutrition.get("Iron") : 0.0); + nutritionParams.put("vitaminA", nutrition.get("VitaminA")); + nutritionParams.put("vitaminB1", nutrition.get("VitaminB1")); + nutritionParams.put("vitaminB2", nutrition.get("VitaminB2")); + nutritionParams.put("niacin", nutrition.get("Niacin")); + nutritionParams.put("vitaminC", nutrition.get("VitaminC")); + nutritionParams.put("volume", nutrition.get("Nutrient_Volume")); + nutritionParams.put("unit", nutrition.get("Nutrient_Volume_Unit")); + + jdbcDao.executeUpdate(nutritionSql.toString(), nutritionParams); + } + } + } + /** + * 获取特定FB_id的所有菜单项 + */ + public List> getFbMenus(Long fbId) { + List> result = new ArrayList<>(); + + // 1. 获取基本菜单信息和国际化描述,使用GROUP BY避免重复 + StringBuilder sql = new StringBuilder( + "SELECT m.*, " + + "MAX(CASE WHEN i.language = 'en' THEN i.value END) as en_description, " + + "MAX(CASE WHEN i.language = 'zh' THEN i.value END) as zh_description " + + "FROM fb_menu m " + + "LEFT JOIN i18n i ON m.FB_menu_id = i.record_id " + + "AND i.table_name = 'fb_menu' " + + "AND i.field_name = 'Menu_Item_Description' " + + "WHERE m.FB_id = :fbId " + + "GROUP BY m.FB_id, m.FB_menu_id, m.Menu_Item_Description, m.created_at"); + + Map params = new HashMap<>(); + params.put("fbId", fbId); + + List> menuItems = jdbcDao.queryForList(sql.toString(), params); + + if (menuItems.isEmpty()) { + return result; + } + + // 2. 获取所有相关的菜单ID + List menuIds = menuItems.stream() + .map(menu -> ((Number) menu.get("FB_menu_id")).longValue()) + .collect(Collectors.toList()); + + // 3. 获取营养成分信息 + StringBuilder nutritionSql = new StringBuilder( + "SELECT * FROM fb_menu_item_nutrition WHERE FB_menu_id IN (:menuIds)"); + Map batchParams = new HashMap<>(); + batchParams.put("menuIds", menuIds); + List> allNutritions = nutritionService.search(batchParams); + + // 4. 获取配料信息,使用GROUP BY避免重复 + StringBuilder ingredientSql = new StringBuilder( + "SELECT i.*, ing.Ingredient_Name, " + + "MAX(CASE WHEN i_en.language = 'en' THEN i_en.value END) as en_Ingredient_Name, " + + "MAX(CASE WHEN i_zh.language = 'zh' THEN i_zh.value END) as zh_Ingredient_Name " + + "FROM fb_menu_item_ingredient i " + + "LEFT JOIN Ingredient ing ON i.ingredient_id = ing.Ingredient_id " + + "LEFT JOIN i18n i_en ON i.ingredient_id = i_en.record_id " + + "AND i_en.table_name = 'Ingredient' " + + "AND i_en.field_name = 'Ingredient_Name' " + + "LEFT JOIN i18n i_zh ON i.ingredient_id = i_zh.record_id " + + "AND i_zh.table_name = 'Ingredient' " + + "AND i_zh.field_name = 'Ingredient_Name' " + + "WHERE i.FB_menu_id IN (:menuIds) " + + "GROUP BY i.id, i.FB_menu_id, i.Ingredient_id, i.Ingredient_Volume, " + + "i.Ingredient_Volume_Unit, i.Allergy_Substance_ID, i.created_at, " + + "ing.Ingredient_Name"); + + List> allIngredients = ingredientService.search(batchParams); + + // 5. 组装每个菜单项的完整信息 + for (Map menu : menuItems) { + Long menuId = ((Number) menu.get("FB_menu_id")).longValue(); + Map completeMenu = new HashMap<>(); + + completeMenu.put("menu", menu); + + // 添加营养成分信息 + List> menuNutritions = allNutritions.stream() + .filter(n -> menuId.equals(((Number) n.get("FB_menu_id")).longValue())) + .collect(Collectors.toList()); + completeMenu.put("nutritions", menuNutritions); + + // 添加配料信息 + List> menuIngredients = allIngredients.stream() + .filter(i -> menuId.equals(((Number) i.get("FB_menu_id")).longValue())) + .collect(Collectors.toList()); + completeMenu.put("ingredients", menuIngredients); + + result.add(completeMenu); + } + + return result; + } + + public List> getOperatorsWithOutletsAndMenus() { + List> result = new ArrayList<>(); + + // 1. 查询所有 operator + List> operators = jdbcDao.queryForList("SELECT * FROM fboperator WHERE deleted=0", new HashMap<>()); + Map> operatorMap = operators.stream().collect(Collectors.toMap( + m -> ((Number)m.get("id")).intValue(), + m -> m + )); + + // 2. 查询 operator 多语言 + Map operatorNameEnMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fboperator".equals(i.get("table_name")) && "FB_name".equals(i.get("field_name")) && "en".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + Map operatorNameZhMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fboperator".equals(i.get("table_name")) && "FB_name".equals(i.get("field_name")) && "zh".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + + // 3. 查询所有 brand + List> brands = jdbcDao.queryForList("SELECT * FROM fb_brand WHERE deleted=0", new HashMap<>()); + Map>> operatorIdToBrands = new HashMap<>(); + for (Map brand : brands) { + Integer operatorId = ((Number)brand.get("FB_operator_id")).intValue(); + operatorIdToBrands.computeIfAbsent(operatorId, k -> new ArrayList<>()).add(brand); + } + + // 4. 查询 brand 多语言 + Map brandNameEnMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fb_brand".equals(i.get("table_name")) && "FB_name".equals(i.get("field_name")) && "en".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + Map brandNameZhMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fb_brand".equals(i.get("table_name")) && "FB_name".equals(i.get("field_name")) && "zh".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + + // 5. 查询所有 outlet + List> outlets = jdbcDao.queryForList("SELECT * FROM fb_outlets WHERE deleted=0", new HashMap<>()); + Map>> brandIdToOutlets = new HashMap<>(); + for (Map outlet : outlets) { + Integer brandId = ((Number)outlet.get("FB_id")).intValue(); + brandIdToOutlets.computeIfAbsent(brandId, k -> new ArrayList<>()).add(outlet); + } + + // 6. 查询 outlet 多语言 + Map outletDistrictEnMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fb_outlets".equals(i.get("table_name")) && "FB_outlet_district".equals(i.get("field_name")) && "en".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + Map outletDistrictZhMap = i18nService.search(new HashMap<>()).stream() + .filter(i -> "fb_outlets".equals(i.get("table_name")) && "FB_outlet_district".equals(i.get("field_name")) && "zh".equals(i.get("language"))) + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).intValue(), + m -> (String)m.get("value") + )); + + // 7. 查询所有菜单-门店关系 + List> relationships = jdbcDao.queryForList("SELECT * FROM fb_menu_and_outlet_relationship", new HashMap<>()); + Map> outletIdToMenuIds = new HashMap<>(); + for (Map rel : relationships) { + Integer outletId = ((Number)rel.get("FB_outlet_id")).intValue(); + Integer menuId = ((Number)rel.get("FB_menu_id")).intValue(); + outletIdToMenuIds.computeIfAbsent(outletId, k -> new ArrayList<>()).add(menuId); + } + + // 8. 组装 + for (Map operator : operators) { + Map op = new LinkedHashMap<>(); + op.put("id", operator.get("id")); + op.put("FB_name_en", operatorNameEnMap.getOrDefault(operator.get("id"), "")); + op.put("FB_name_zh", operatorNameZhMap.getOrDefault(operator.get("id"), "")); + List> brandList = new ArrayList<>(); + List> brandsOfOp = operatorIdToBrands.get(operator.get("id")); + if (brandsOfOp != null) { + for (Map brand : brandsOfOp) { + Map br = new LinkedHashMap<>(); + br.put("id", brand.get("id")); + br.put("FB_operator_id", brand.get("FB_operator_id")); + br.put("FB_name_en", brandNameEnMap.getOrDefault(brand.get("id"), "")); + br.put("FB_name_zh", brandNameZhMap.getOrDefault(brand.get("id"), "")); + List> outletList = new ArrayList<>(); + List> outletsOfBrand = brandIdToOutlets.get(brand.get("id")); + if (outletsOfBrand != null) { + for (Map outlet : outletsOfBrand) { + Map out = new LinkedHashMap<>(); + out.put("id", outlet.get("id")); + out.put("FB_id", outlet.get("FB_id")); + out.put("FB_outlet_district_en", outletDistrictEnMap.getOrDefault(outlet.get("id"), "")); + out.put("FB_outlet_district_zh", outletDistrictZhMap.getOrDefault(outlet.get("id"), "")); + List> menuList = new ArrayList<>(); + List menuIds = outletIdToMenuIds.get(outlet.get("id")); + if (menuIds != null) { + for (Integer menuId : menuIds) { + Map m = new HashMap<>(); + m.put("FB_menu_id", menuId); + menuList.add(m); + } + } + out.put("menus", menuList); + outletList.add(out); + } + } + br.put("outlets", outletList); + brandList.add(br); + } + } + op.put("brands", brandList); + result.add(op); + } + return result; + } + + public void saveMenuOutletRelationshipsByBatch(Map data) { + Long fbId = data.get("FB_id") instanceof Number ? ((Number) data.get("FB_id")).longValue() : null; + if (fbId == null) throw new IllegalArgumentException("FB_id 不能为空"); + + List> outlets = (List>) data.get("outlets"); + if (outlets == null) throw new IllegalArgumentException("outlets 不能为空"); + + // 1. 删除该FB_id下所有现有关系 + jdbcDao.executeUpdate( + "DELETE FROM fb_menu_and_outlet_relationship WHERE FB_id = :fbId", + Map.of("fbId", fbId) + ); + + // 2. 批量插入新关系 + for (Map outlet : outlets) { + Object outletIdObj = outlet.get("FB_outlet_id"); + if (outletIdObj == null) { + throw new IllegalArgumentException("outlet缺少FB_outlet_id: " + outlet); + } + Long outletId = ((Number) outletIdObj).longValue(); + + List> menus = (List>) outlet.get("menus"); + if (menus == null) { + throw new IllegalArgumentException("outlet缺少menus: " + outlet); + } + for (Map menu : menus) { + Object menuIdObj = menu.get("FB_menu_id"); + if (menuIdObj == null) { + throw new IllegalArgumentException("menu缺少FB_menu_id: " + menu + " in outlet: " + outlet); + } + Long menuId = ((Number) menuIdObj).longValue(); + saveMenuOutletRelationship(menuId, outletId, fbId); + } + } + } + + /** + * 删除菜品和门店的关联 + */ + @Transactional(rollbackFor = Exception.class) + public void deleteMenuOutletRelationship(Long menuId, Long outletId) { + //logger.info("Deleting menu-outlet relationship: menuId={}, outletId={}", menuId, outletId); + + Map params = new HashMap<>(); + params.put("id", menuId); + params.put("outletId", outletId); + + String deleteSql = "DELETE FROM fb_menu_and_outlet_relationship " + + "WHERE id = :menuId AND FB_outlet_id = :outletId"; + + jdbcDao.executeUpdate(deleteSql, params); + //logger.info("Successfully deleted menu-outlet relationship"); + } + @Transactional(rollbackFor = Exception.class) + public void deleteMenu(Long menuId) { + //logger.info("Deleting menu and related data: menuId={}", menuId); + + Map params = new HashMap<>(); + params.put("menuId", menuId); + + try { + // 1. 删除菜品和门店的关联 + String deleteRelationshipSql = "DELETE FROM fb_menu_and_outlet_relationship " + + "WHERE FB_menu_id = :menuId"; + jdbcDao.executeUpdate(deleteRelationshipSql, params); + //logger.info("Deleted menu-outlet relationships"); + + // 2. 删除菜品的营养信息 + String deleteNutritionSql = "DELETE FROM fb_menu_item_nutrition " + + "WHERE FB_menu_id = :menuId"; + jdbcDao.executeUpdate(deleteNutritionSql, params); + //logger.info("Deleted menu nutrition data"); + + // 3. 删除菜品的配料信息 + String deleteIngredientSql = "DELETE FROM fb_menu_item_ingredient " + + "WHERE FB_menu_id = :menuId"; + jdbcDao.executeUpdate(deleteIngredientSql, params); + //logger.info("Deleted menu ingredient data"); + + // 4. 删除菜品的国际化信息 + String deleteI18nSql = "DELETE FROM i18n " + + "WHERE table_name = 'fb_menu' AND record_id = :menuId"; + jdbcDao.executeUpdate(deleteI18nSql, params); + //logger.info("Deleted menu i18n data"); + + // 5. 删除菜品本身 + String deleteMenuSql = "DELETE FROM fb_menu WHERE id = :menuId"; + jdbcDao.executeUpdate(deleteMenuSql, params); + //logger.info("Deleted menu"); + + } catch (Exception e) { + //logger.error("Error deleting menu: menuId={}", menuId, e); + throw new RuntimeException("Failed to delete menu", e); + } + } + + public List> getMenuListWithDetails() { + // 1. 查所有菜单 + List> menus = menuService.search(new HashMap<>()); + + // 2. 查所有营养 + List> nutritions = nutritionService.search(new HashMap<>()); + + // 3. 查所有配料 + List> ingredients = ingredientService.search(new HashMap<>()); + + // 4. 查所有门店关系 + List> relationships = relationshipService.search(new HashMap<>()); + + // 5. 查所有i18n(菜单描述、配料名) + Map menuI18nArgs = new HashMap<>(); + menuI18nArgs.put("table_name", "fb_menu"); + List> menuI18n = i18nService.search(menuI18nArgs); + + // 7. 组装 + List> result = new ArrayList<>(); + for (Map menu : menus) { + Long menuId = ((Number) menu.get("id")).longValue(); + Map item = new LinkedHashMap<>(); + item.put("id", menuId); + + // 多语言描述 + // 1. 先查询 Menu_Item_Description 的多语言 + item.put("Menu_Item_Description_en", menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == menuId + && "Menu_Item_Description".equals(i.get("field_name")) + && "en".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("")); + + item.put("Menu_Item_Description_zh", menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == menuId + && "Menu_Item_Description".equals(i.get("field_name")) + && "zh".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("")); + + // 2. 再查询 Category 的多语言 + item.put("category_en", menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == menuId + && "category".equals(i.get("field_name")) + && "en".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("")); + + item.put("category_zh", menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == menuId + && "category".equals(i.get("field_name")) + && "zh".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("")); + // 营养 + Map nutrition = nutritions.stream().filter(n -> ((Number)n.get("FB_menu_id")).longValue() == menuId).findFirst().orElse(new HashMap<>()); + // 只放你需要的字段 + item.put("Calories", nutrition.getOrDefault("Calories", null)); + item.put("Protein", nutrition.getOrDefault("Protein", null)); + item.put("Fat", nutrition.getOrDefault("Fat", null)); + item.put("Carbohydrate", nutrition.getOrDefault("Carbohydrate", null)); + item.put("Sodium", nutrition.getOrDefault("Sodium", null)); + item.put("Potassium", nutrition.getOrDefault("Potassium", null)); + item.put("Calcium", nutrition.getOrDefault("Calcium", null)); + item.put("Phosphorus", nutrition.getOrDefault("Phosphorus", null)); + item.put("Iron", nutrition.getOrDefault("Iron", null)); + item.put("VitaminA", nutrition.getOrDefault("VitaminA", null)); + item.put("VitaminB1", nutrition.getOrDefault("VitaminB1", null)); + item.put("VitaminB2", nutrition.getOrDefault("VitaminB2", null)); + item.put("Niacin", nutrition.getOrDefault("Niacin", null)); + item.put("VitaminC", nutrition.getOrDefault("VitaminC", null)); + item.put("Nutrient_Volume", nutrition.getOrDefault("Nutrient_Volume", null)); + item.put("Nutrient_Volume_Unit", nutrition.getOrDefault("Nutrient_Volume_Unit", null)); + + // 价格、标签等 + item.put("price", menu.get("price")); + item.put("is_vegan", menu.get("is_vegan")); + item.put("is_gluten_free", menu.get("is_gluten_free")); + item.put("is_spicy", menu.get("is_spicy")); + item.put("is_vegetarian", menu.get("is_vegetarian")); + // 门店列表 + List outletList = relationships.stream() + .filter(r -> ((Number)r.get("FB_menu_id")).longValue() == menuId) + .map(r -> ((Number)r.get("FB_outlet_id")).longValue()) + .collect(Collectors.toList()); + item.put("outletList", outletList); + + // 配料列表 + List> ingredientList = ingredients.stream() + .filter(ing -> ((Number)ing.get("FB_menu_id")).longValue() == menuId) + .map(ing -> { + Map ingItem = new HashMap<>(); + Long ingId = ((Number)ing.get("Ingredient_id")).longValue(); + ingItem.put("Ingredient_id", ingId); + ingItem.put( + "Ingredient_Name_en", + menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == ingId && "Ingredient_Name".equals(i.get("field_name")) && "en".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("") + ); + ingItem.put( + "Ingredient_Name_zh", + menuI18n.stream() + .filter(i -> ((Number)i.get("record_id")).longValue() == ingId && "Ingredient_Name".equals(i.get("field_name")) && "zh".equals(i.get("language"))) + .map(i -> i.get("value")) + .findFirst() + .orElse("") + ); + ingItem.put("Ingredient_Volume", ing.get("Ingredient_Volume")); + ingItem.put("Ingredient_Volume_Unit", ing.get("Ingredient_Volume_Unit")); + ingItem.put("Allergy_Substance_ID", ing.get("Allergy_Substance_ID")); + return ingItem; + }) + .collect(Collectors.toList()); + item.put("ingredients", ingredientList); + + result.add(item); + } + return result; + } + + @Transactional(rollbackFor = Exception.class) + public void saveMenuWithDetails(Map menuData) { + // 1. 更新 fb_menu 表的基本信息 + StringBuilder updateMenuSql = new StringBuilder( + "UPDATE fb_menu SET " + + "Menu_Item_Description = :description, " + + "category = :category, " + + "price = :price, " + + "is_vegetarian = :is_vegetarian, " + + "is_vegan = :is_vegan, " + + "is_gluten_free = :is_gluten_free, " + + "is_spicy = :is_spicy, " + + "modified = NOW() " + + "WHERE id = :id"); + + Map menuParams = new HashMap<>(); + menuParams.put("id", menuData.get("id")); + menuParams.put("description", menuData.get("Menu_Item_Description_en")); // 使用英文描述作为默认值 + menuParams.put("category", menuData.get("category_en")); // 使用英文分类作为默认值 + menuParams.put("price", menuData.get("price")); + menuParams.put("is_vegetarian", menuData.get("is_vegetarian")); + menuParams.put("is_vegan", menuData.get("is_vegan")); + menuParams.put("is_gluten_free", menuData.get("is_gluten_free")); + menuParams.put("is_spicy", menuData.get("is_spicy")); + + jdbcDao.executeUpdate(updateMenuSql.toString(), menuParams); + Long menuId = ((Number) menuData.get("id")).longValue(); + + // 2. 保存菜单描述的国际化信息 + // 英文描述 + upsertI18n("fb_menu", "Menu_Item_Description", menuId, "en", (String) menuData.get("Menu_Item_Description_en")); + + // 中文描述 + upsertI18n("fb_menu", "Menu_Item_Description", menuId, "zh", (String) menuData.get("Menu_Item_Description_zh")); + + // 3. 保存分类的国际化信息 + // 英文分类 + upsertI18n("fb_menu", "category", menuId, "en", (String) menuData.get("category_en")); + + // 中文分类 + upsertI18n("fb_menu", "category", menuId, "zh", (String) menuData.get("category_zh")); + + // 4. 保存配料信息 + List> ingredients = (List>) menuData.get("ingredients"); + if (ingredients != null) { + // 删除旧的配料信息 + jdbcDao.executeUpdate( + "DELETE FROM fb_menu_item_ingredient WHERE FB_menu_id = :menuId", + Map.of("menuId", menuId) + ); + + for (Map ingredient : ingredients) { + try { + Long ingredientId = ((Number) ingredient.get("Ingredient_id")).longValue(); + + // 更新 Ingredient 表中的过敏原信息 + List allergyIds = (List) ingredient.get("Allergy_Substance_IDs"); + String allergyJson = allergyIds != null ? new ObjectMapper().writeValueAsString(allergyIds) : "[]"; + + StringBuilder updateIngredientSql = new StringBuilder( + "UPDATE Ingredient SET Allergy_Substance_ID = :allergyJson " + + "WHERE Ingredient_id = :ingredientId"); + Map updateParams = new HashMap<>(); + updateParams.put("allergyJson", allergyJson); + updateParams.put("ingredientId", ingredientId); + jdbcDao.executeUpdate(updateIngredientSql.toString(), updateParams); + + // 保存配料关联信息 + fb_menu_item_ingredientReq ingredientReq = new fb_menu_item_ingredientReq(); + ingredientReq.setFB_menu_id(menuId); + ingredientReq.setIngredient_id(ingredientId); + ingredientReq.setIngredient_Volume(ingredient.get("Ingredient_Volume") != null ? + new BigDecimal(ingredient.get("Ingredient_Volume").toString()) : null); + ingredientReq.setIngredient_Volume_Unit((String) ingredient.get("Ingredient_Volume_Unit")); + + // 直接使用 SQL 插入,避免使用 service 层的自动字段处理 + StringBuilder insertIngredientSql = new StringBuilder( + "INSERT INTO fb_menu_item_ingredient " + + "(FB_menu_id, Ingredient_id, Ingredient_Volume, Ingredient_Volume_Unit, created_at) " + + "VALUES (:menuId, :ingredientId, :volume, :unit, NOW())"); + + Map insertParams = new HashMap<>(); + insertParams.put("menuId", menuId); + insertParams.put("ingredientId", ingredientId); + insertParams.put("volume", ingredientReq.getIngredient_Volume()); + insertParams.put("unit", ingredientReq.getIngredient_Volume_Unit()); + + jdbcDao.executeUpdate(insertIngredientSql.toString(), insertParams); + + // 保存配料名称的国际化信息 + i18nReq enIngredientReq = new i18nReq(); + enIngredientReq.setTable_name("Ingredient"); + enIngredientReq.setRecord_id(ingredientId); + enIngredientReq.setField_name("Ingredient_Name"); + enIngredientReq.setLanguage("en"); + enIngredientReq.setValue((String) ingredient.get("Ingredient_Name_en")); + i18nService.saveOrUpdate(enIngredientReq); + + i18nReq zhIngredientReq = new i18nReq(); + zhIngredientReq.setTable_name("Ingredient"); + zhIngredientReq.setRecord_id(ingredientId); + zhIngredientReq.setField_name("Ingredient_Name"); + zhIngredientReq.setLanguage("zh"); + zhIngredientReq.setValue((String) ingredient.get("Ingredient_Name_zh")); + i18nService.saveOrUpdate(zhIngredientReq); + } catch (JsonProcessingException e) { + logger.error("Error processing allergy IDs for ingredient: " + ingredient.get("Ingredient_id"), e); + throw new RuntimeException("Failed to process allergy IDs", e); + } + } + } + } + + private void upsertI18n(String table, String field, Long recordId, String lang, String value) { + // 1. 先查 + Map args = new HashMap<>(); + args.put("table_name", table); + args.put("field_name", field); + args.put("record_id", recordId); + args.put("language", lang); + List> exist = i18nService.search(args); + + if (exist != null && !exist.isEmpty()) { + // 有则更新 + i18nService.updateValue(table, field, recordId, lang, value); + } else { + // 没有则插入 + i18nReq req = new i18nReq(); + req.setTable_name(table); + req.setField_name(field); + req.setRecord_id(recordId); + req.setLanguage(lang); + req.setValue(value); + i18nService.saveOrUpdate(req); + } + } + + @Transactional(rollbackFor = Exception.class) + public void saveGroupRelationships(Map data) { + Long fbBrandId = ((Number) data.get("Fb_brand_id")).longValue(); + List> groups = (List>) data.get("Group"); + + // 验证品牌是否存在 + StringBuilder checkBrandSql = new StringBuilder( + "SELECT COUNT(*) FROM fb_brand WHERE id = :fbBrandId"); + Map checkBrandParams = new HashMap<>(); + checkBrandParams.put("fbBrandId", fbBrandId); + int brandCount = jdbcDao.queryForInt(checkBrandSql.toString(), checkBrandParams); + if (brandCount == 0) { + throw new RuntimeException("Brand not found: " + fbBrandId); + } + + // 验证所有分组是否存在 + for (Map group : groups) { + Long groupId = ((Number) group.get("FB_group_id")).longValue(); + StringBuilder checkGroupSql = new StringBuilder( + "SELECT COUNT(*) FROM fb_group WHERE id = :groupId"); + Map checkGroupParams = new HashMap<>(); + checkGroupParams.put("groupId", groupId); + int groupCount = jdbcDao.queryForInt(checkGroupSql.toString(), checkGroupParams); + if (groupCount == 0) { + throw new RuntimeException("Group not found: " + groupId); + } + } + + // 验证所有菜单是否存在 + for (Map group : groups) { + List menuIds = (List) group.get("id"); + if (menuIds != null && !menuIds.isEmpty()) { + StringBuilder checkMenuSql = new StringBuilder( + "SELECT COUNT(*) FROM fb_menu WHERE id IN (:menuIds)"); + Map checkMenuParams = new HashMap<>(); + checkMenuParams.put("menuIds", menuIds); + int menuCount = jdbcDao.queryForInt(checkMenuSql.toString(), checkMenuParams); + if (menuCount != menuIds.size()) { + throw new RuntimeException("Some menus not found"); + } + } + } + + // 删除现有关系 + StringBuilder deleteSql = new StringBuilder( + "DELETE FROM fb_group_relationship WHERE Fb_brand_id = :fbBrandId"); + Map deleteParams = new HashMap<>(); + deleteParams.put("fbBrandId", fbBrandId); + jdbcDao.executeUpdate(deleteSql.toString(), deleteParams); + + // 插入新关系 + for (Map group : groups) { + Long groupId = ((Number) group.get("FB_group_id")).longValue(); + List menuIds = (List) group.get("FB_menu_id"); + + if (menuIds != null && !menuIds.isEmpty()) { + StringBuilder insertSql = new StringBuilder( + "INSERT INTO fb_group_relationship " + + "(Fb_brand_id, FB_group_id, FB_menu_id, created) " + + "VALUES (:fbBrandId, :groupId, :menuId, NOW())"); + + for (Number menuId : menuIds) { + Map insertParams = new HashMap<>(); + insertParams.put("fbBrandId", fbBrandId); + insertParams.put("groupId", groupId); + insertParams.put("menuId", menuId); + jdbcDao.executeUpdate(insertSql.toString(), insertParams); + } + } + } + } + + @Transactional(rollbackFor = Exception.class) + public void saveGroupShowControl(Long brandId, List> groupShowList) { + // 先删除该品牌下所有分组的 show 控制 + String deleteSql = "DELETE FROM fb_group_show_control WHERE FB_brand_id = :brandId"; + Map params = new HashMap<>(); + params.put("brandId", brandId); + jdbcDao.executeUpdate(deleteSql, params); + + // 只插入 show_table 为 true 的记录 + String insertSql = "INSERT INTO fb_group_show_control (FB_brand_id, FB_group_id, show_table) VALUES (:brandId, :groupId, 1)"; + for (Map groupShow : groupShowList) { + // 只处理 show_table 为 true 的记录 + if (Boolean.TRUE.equals(groupShow.get("show_table"))) { + Map insertParams = new HashMap<>(); + insertParams.put("brandId", brandId); + insertParams.put("groupId", groupShow.get("FB_group_id")); + jdbcDao.executeUpdate(insertSql, insertParams); + } + } + } + + public List> getGroupShowControl(Long brandId) { + // 获取所有分组 + String groupSql = "SELECT id as FB_group_id FROM fb_group WHERE deleted = 0 AND (FB_brand_id = :brandId OR FB_brand_id IS NULL OR FB_brand_id = 0)"; + Map params = new HashMap<>(); + params.put("brandId", brandId); + List> allGroups = jdbcDao.queryForList(groupSql, params); + + // 获取已选中的分组 + String selectedSql = "SELECT FB_group_id FROM fb_group_show_control WHERE FB_brand_id = :brandId"; + List> selectedGroups = jdbcDao.queryForList(selectedSql, params); + + // 标记选中的分组 + Set selectedGroupIds = selectedGroups.stream() + .map(g -> ((Number) g.get("FB_group_id")).longValue()) + .collect(Collectors.toSet()); + + // 组装结果 + return allGroups.stream() + .map(group -> { + Map result = new HashMap<>(group); + result.put("show_table", selectedGroupIds.contains(((Number) group.get("FB_group_id")).longValue())); + return result; + }) + .collect(Collectors.toList()); + } + + public List> getGroupShowControlWithMenus(Long brandId, Boolean showTable) { + // 1. 获取所有分组(当前品牌+默认分组) + String groupSql = "SELECT id as FB_group_id FROM fb_group WHERE deleted = 0 AND (FB_brand_id = :brandId OR FB_brand_id IS NULL OR FB_brand_id = 0)"; + Map params = new HashMap<>(); + params.put("brandId", brandId); + List> allGroups = jdbcDao.queryForList(groupSql, params); + + // 2. 获取已选中的分组 + String selectedSql = "SELECT FB_group_id FROM fb_group_show_control WHERE FB_brand_id = :brandId"; + List> selectedGroups = jdbcDao.queryForList(selectedSql, params); + Set selectedGroupIds = selectedGroups.stream() + .map(g -> ((Number) g.get("FB_group_id")).longValue()) + .collect(Collectors.toSet()); + + // 获取所有分组-菜单关系 + String relSql = "SELECT FB_group_id, FB_menu_id FROM fb_group_relationship WHERE Fb_brand_id = :brandId"; + List> groupMenuRels = jdbcDao.queryForList(relSql, params); + + // 获取所有菜单 + String menuSql = "SELECT m.id as FB_menu_id, " + + "i_en.value AS Menu_Item_Description_en, " + + "i_zh.value AS Menu_Item_Description_zh " + + "FROM fb_menu m " + + "LEFT JOIN i18n i_en ON m.id = i_en.record_id " + + " AND i_en.table_name = 'fb_menu' " + + " AND i_en.field_name = 'Menu_Item_Description' " + + " AND i_en.language = 'en' " + + "LEFT JOIN i18n i_zh ON m.id = i_zh.record_id " + + " AND i_zh.table_name = 'fb_menu' " + + " AND i_zh.field_name = 'Menu_Item_Description' " + + " AND i_zh.language = 'zh'"; + List> allMenus = jdbcDao.queryForList(menuSql, new HashMap<>()); + Map> menuMap = allMenus.stream() + .collect(Collectors.toMap( + m -> ((Number)m.get("FB_menu_id")).longValue(), + m -> m + )); + + // 查询所有分组的英文和中文名称 + String groupNameEnSql = "SELECT record_id, value FROM i18n WHERE table_name = 'fb_group' AND field_name = 'FB_group_name' AND language = 'en'"; + String groupNameZhSql = "SELECT record_id, value FROM i18n WHERE table_name = 'fb_group' AND field_name = 'FB_group_name' AND language = 'zh'"; + List> groupNameEnList = jdbcDao.queryForList(groupNameEnSql, new HashMap<>()); + List> groupNameZhList = jdbcDao.queryForList(groupNameZhSql, new HashMap<>()); + + // 转为Map方便查找 + Map groupNameEnMap = groupNameEnList.stream() + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).longValue(), + m -> (String)m.get("value"), + (v1, v2) -> v1 + )); + Map groupNameZhMap = groupNameZhList.stream() + .collect(Collectors.toMap( + m -> ((Number)m.get("record_id")).longValue(), + m -> (String)m.get("value"), + (v1, v2) -> v1 + )); + + // 组装每个分组的菜单 + List> resultGroups = new ArrayList<>(); + for (Map group : allGroups) { + Long groupId = ((Number) group.get("FB_group_id")).longValue(); + boolean isSelected = selectedGroupIds.contains(groupId); + + // 如果 showTable 参数为 true,则只包含 show_table=true 的分组 + if (showTable != null && showTable && !isSelected) { + continue; + } + + Map orderedGroup = new LinkedHashMap<>(); + orderedGroup.put("FB_group_id", groupId); + orderedGroup.put("FB_group_Name_en", groupNameEnMap.getOrDefault(groupId, "")); + orderedGroup.put("FB_group_Name_zh", groupNameZhMap.getOrDefault(groupId, "")); + orderedGroup.put("show_table", isSelected); + + List> menus = groupMenuRels.stream() + .filter(rel -> ((Number)rel.get("FB_group_id")).longValue() == groupId) + .map(rel -> menuMap.get(((Number)rel.get("FB_menu_id")).longValue())) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + orderedGroup.put("menus", menus); + + resultGroups.add(orderedGroup); + } + return resultGroups; + } + + public List> getBrandMenusSimple(Long FB_id) { + // 1. 获取所有菜单的多语言详情 + List> allMenus = getMenuListWithDetails(); + + // 2. 获取该品牌所有菜单ID + String relSql = "SELECT DISTINCT FB_menu_id FROM fb_menu_and_outlet_relationship WHERE FB_id = :FB_id"; + Map params = new HashMap<>(); + params.put("FB_id", FB_id); + List> rels = jdbcDao.queryForList(relSql, params); + + // 添加空值检查 + Set brandMenuIds = rels.stream() + .map(r -> { + Object menuId = r.get("FB_menu_id"); + if (menuId == null) { + logger.warn("发现空的菜单ID,FB_id: {}", FB_id); + return null; + } + return ((Number)menuId).intValue(); + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 3. 获取该品牌所有菜单-分组关系 + String groupRelSql = "SELECT FB_menu_id, FB_group_id FROM fb_group_relationship WHERE Fb_brand_id = :FB_id"; + List> groupRels = jdbcDao.queryForList(groupRelSql, params); + Map> menuIdToGroupIds = new HashMap<>(); + + for (Map gr : groupRels) { + Object menuId = gr.get("FB_menu_id"); + Object groupId = gr.get("FB_group_id"); + if (menuId != null && groupId != null) { + Integer menuIdInt = ((Number)menuId).intValue(); + Integer groupIdInt = ((Number)groupId).intValue(); + menuIdToGroupIds.computeIfAbsent(menuIdInt, k -> new ArrayList<>()).add(groupIdInt); + } + } + + // 4. 组装输出 + List> result = allMenus.stream() + .filter(menu -> { + Object menuId = menu.get("FB_menu_id"); + if (menuId == null) { + logger.warn("菜单数据中缺少FB_menu_id"); + return false; + } + return brandMenuIds.contains(((Number)menuId).intValue()); + }) + .map(menu -> { + Map simple = new HashMap<>(); + Integer menuId = ((Number)menu.get("FB_menu_id")).intValue(); + simple.put("FB_menu_id", menuId); + simple.put("Menu_Item_Description_en", menu.get("Menu_Item_Description_en")); + simple.put("Menu_Item_Description_zh", menu.get("Menu_Item_Description_zh")); + simple.put("FB_group_id", menuIdToGroupIds.getOrDefault(menuId, new ArrayList<>())); + return simple; + }) + .collect(Collectors.toList()); + + return result; + } + + @Transactional(rollbackFor = Exception.class) + public void saveMenuOutletRelationship(Long menuId, Long outletId, Long fbId) { + Map params = new HashMap<>(); + params.put("menuId", menuId); + params.put("outletId", outletId); + params.put("fbId", fbId); + + // 检查是否已存在 + String checkSql = "SELECT COUNT(*) FROM fb_menu_and_outlet_relationship WHERE FB_menu_id = :menuId AND FB_outlet_id = :outletId"; + int count = jdbcDao.queryForInt(checkSql, params); + if (count == 0) { + // 插入新关系 + String insertSql = "INSERT INTO fb_menu_and_outlet_relationship (FB_menu_id, FB_outlet_id, FB_id, created_at) VALUES (:menuId, :outletId, :fbId, NOW())"; + jdbcDao.executeUpdate(insertSql, params); + } + } + public void saveMenuOutletRelationshipsByMenu(Map data) { + Long menuId = data.get("FB_menu_id") instanceof Number ? ((Number) data.get("FB_menu_id")).longValue() : null; + if (menuId == null) throw new IllegalArgumentException("FB_menu_id 不能为空"); + + List> outlets = (List>) data.get("outlets"); + if (outlets == null) throw new IllegalArgumentException("outlets 不能为空"); + + // 1. 删除该菜单的所有门店关系 + jdbcDao.executeUpdate( + "DELETE FROM fb_menu_and_outlet_relationship WHERE FB_menu_id = :menuId", + Map.of("menuId", menuId) + ); + + // 2. 批量插入新关系 + for (Map outlet : outlets) { + Long fbId = outlet.get("FB_id") instanceof Number ? ((Number) outlet.get("FB_id")).longValue() : null; + Long outletId = outlet.get("FB_outlet_id") instanceof Number ? ((Number) outlet.get("FB_outlet_id")).longValue() : null; + if (fbId == null || outletId == null) continue; + saveMenuOutletRelationship(menuId, outletId, fbId); + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_groupService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_groupService.java new file mode 100644 index 0000000..487cd3c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_groupService.java @@ -0,0 +1,139 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import com.ffii.fhsmsc.modules.Menu.enity.fb_group; +import com.ffii.fhsmsc.modules.Menu.enity.fb_groupResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_groupReq; + +import jakarta.validation.Valid; +@Service + public class fb_groupService extends AbstractBaseEntityService { + private final i18nService i18nService; + private static final Logger logger = LoggerFactory.getLogger(fb_groupService.class); + public fb_groupService(JdbcDao jdbcDao, fb_groupResponsity repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `fb_group` fi" + + " WHERE 1=1"); + + if (args != null && args.containsKey("FB_brand_id")) { + // 修改SQL,使用OR条件来匹配brand_id或null + sql.append(" AND (fi.FB_brand_id = :FB_brand_id OR fi.FB_brand_id IS NULL)"); + } + + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_group saveOrUpdate(@Valid fb_groupReq req) { + logger.info("Request: Id={}, FB_group_Name_en={}, FB_group_Name_zh={}, FB_brand_id={}", + req.getId(), req.getFB_group_Name_en(), req.getFB_group_Name_zh(), req.getFB_brand_id()); + + fb_group instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(() -> + new InternalServerErrorException("Group not found for id: " + req.getId())); + } else { + instance = new fb_group(); + isNew = true; + } + + // 设置基本字段 + instance.setId(req.getId()); + instance.setFB_brand_id(req.getFB_brand_id()); + // 使用英文名称作为默认名称 + instance.setFB_group_Name(req.getFB_group_Name_en()); + + // 设置时间戳 + if (isNew) { + instance.setCreated(LocalDateTime.now()); + } + instance.setModified(LocalDateTime.now()); + + try { + instance = repository.saveAndFlush(instance); + + // 保存多语言信息 + if (req.getFB_group_Name_en() != null) { + updateI18nEntry("fb_group", "FB_group_name", instance.getId(), "en", req.getFB_group_Name_en()); + } + if (req.getFB_group_Name_zh() != null) { + updateI18nEntry("fb_group", "FB_group_name", instance.getId(), "zh", req.getFB_group_Name_zh()); + } + + logger.info("Saved group: id={}, FB_group_Name_en={}, FB_group_Name_zh={}", + instance.getId(), req.getFB_group_Name_en(), req.getFB_group_Name_zh()); + } catch (Exception e) { + logger.error("Failed to save group: id={}, error={}", + req.getId(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save group: " + e.getMessage()); + } + + return instance; + } + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + @Transactional + public void deleteById(Long id) { + repository.deleteById(id); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_relationshipService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_relationshipService.java new file mode 100644 index 0000000..edae7db --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_relationshipService.java @@ -0,0 +1,62 @@ +package com.ffii.fhsmsc.modules.Menu.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.Menu.enity.fb_group_relationship; +import com.ffii.fhsmsc.modules.Menu.enity.fb_group_relationshipResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_group_relationshipReq; + +import jakarta.validation.Valid; +@Service +public class fb_group_relationshipService extends AbstractBaseEntityService { + public fb_group_relationshipService(JdbcDao jdbcDao, fb_group_relationshipResponsity repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.id," + + " fi.FB_brand_id," + + " fi.FB_group_id," + + " fi.show_table" + + " FROM `fb_group_show_control` fi" + + " WHERE 1=1" + ); + + if (args != null) { + if (args.containsKey("FB_brand_id")) { + sql.append(" AND fi.FB_brand_id = :FB_brand_id"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_group_relationship saveOrUpdate(@Valid fb_group_relationshipReq req) { + fb_group_relationship instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_group_relationship(); + } + } else { + instance = new fb_group_relationship(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_show_controlService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_show_controlService.java new file mode 100644 index 0000000..27c6985 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_group_show_controlService.java @@ -0,0 +1,70 @@ +package com.ffii.fhsmsc.modules.Menu.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.Menu.enity.fb_group_show_control; +import com.ffii.fhsmsc.modules.Menu.enity.fb_group_show_controlResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_group_show_controlReq; + +import jakarta.validation.Valid; +@Service +public class fb_group_show_controlService extends AbstractBaseEntityService { + public fb_group_show_controlService(JdbcDao jdbcDao, fb_group_show_controlResponsity repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.id," + + " fi.FB_brand_id," + + " fi.FB_group_id," + + " fi.show_table" + + " FROM `fb_group_show_control` fi" + ); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("FB_group_id")) { + sql.append(" AND fi.FB_group_id = :FB_group_id"); + } + if (args.containsKey("FB_brand_id")) { + sql.append(" AND fi.FB_brand_id = :FB_brand_id"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_group_show_control saveOrUpdate(@Valid fb_group_show_controlReq req) { + fb_group_show_control instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_group_show_control(); + } + } else { + instance = new fb_group_show_control(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menuService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menuService.java new file mode 100644 index 0000000..e3edcf9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menuService.java @@ -0,0 +1,159 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menuResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_menuReq; + +import jakarta.validation.Valid; +@Service + public class fb_menuService extends AbstractBaseEntityService { + private final i18nService i18nService; + private static final Logger logger = LoggerFactory.getLogger(fb_menuService.class); + public fb_menuService(JdbcDao jdbcDao, fb_menuResponsity repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `fb_menu` fi" + ); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND fi.id = :userId"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_menu saveOrUpdate(@Valid fb_menuReq req) { + logger.info("Request: FB_menu_id={}, Menu_Item_Description={}, Menu_Item_Description_zh={}, Category={}, Price={}, Is_vegan={}, Is_vegetarian={}, Is_gluten_free={}, Is_spicy={}", + req.getId(), req.getMenu_Item_Description(), req.getMenu_Item_Description_zh(), + req.getCategory(), req.getPrice(), + req.getIs_vegan(), req.getIs_vegetarian(), req.getIs_gluten_free(), req.getIs_spicy()); + + fb_menu instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(() -> new InternalServerErrorException("Operator not found for id: " + req.getId())); + logger.info("Found existing operator: id={}, Menu_Item_Description={}", instance.getId(), instance.getMenu_Item_Description()); + } else { + instance = new fb_menu(); + isNew = true; + logger.info("Creating new operator"); + } + + // 手动设置字段 + instance.setId(req.getId()); + //instance.setFB_id(req.getFB_id()); + instance.setMenu_Item_Description(req.getMenu_Item_Description()); // 使用英文版本作为默认值 + instance.setCategory(req.getCategory()); // 使用英文版本作为默认值 + instance.setPrice(req.getPrice()); // 使用英文版本作为默认值 + instance.setIs_vegan(req.getIs_vegan()); + instance.setIs_vegetarian(req.getIs_vegetarian()); + instance.setIs_gluten_free(req.getIs_gluten_free()); + instance.setIs_spicy(req.getIs_spicy()); + + logger.info("Before save - Setting values: Menu_Item_Description={}, Category={}, Price={}, Is_vegan={}, Is_vegetarian={}, Is_gluten_free={}, Is_spicy={}", + instance.getMenu_Item_Description(), instance.getCategory(), instance.getPrice(), instance.getIs_vegan(), instance.getIs_vegetarian(), instance.getIs_gluten_free(), instance.getIs_spicy()); + + + + try { + instance = repository.saveAndFlush(instance); + logger.info("After save - Saved operator: id={}, Menu_Item_Description={}, Category={}, Price={}, Is_vegan={}, Is_vegetarian={}, Is_gluten_free={}, Is_spicy={}", + instance.getId(), instance.getMenu_Item_Description(), instance.getCategory(), instance.getPrice(), instance.getIs_vegan(), instance.getIs_vegetarian(), instance.getIs_gluten_free(), instance.getIs_spicy()); + } catch (Exception e) { + logger.error("Failed to save fb_menu: id={}, error={}", req.getId(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save operator: " + e.getMessage()); + } + + Long menuId = instance.getId(); + logger.info("Processing i18n entries for menuId={}", menuId); + + // 更新 i18n 记录 + try { + // Menu_Item_Description 的翻译 + updateI18nEntry("fb_menu", "Menu_Item_Description", menuId, "en", req.getMenu_Item_Description_en()); + updateI18nEntry("fb_menu", "Menu_Item_Description", menuId, "zh", req.getMenu_Item_Description_zh()); + + // Category 的翻译 + updateI18nEntry("fb_menu", "Category", menuId, "en", req.getCategory_en()); + updateI18nEntry("fb_menu", "Category", menuId, "zh", req.getCategory_zh()); + + logger.info("Successfully updated all i18n entries"); + } catch (Exception e) { + logger.error("Failed to update i18n entries: error={}", e.getMessage(), e); + throw new InternalServerErrorException("Failed to update i18n entries: " + e.getMessage()); + } + + return instance; + } + + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_and_outlet_relationshipService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_and_outlet_relationshipService.java new file mode 100644 index 0000000..52efbdd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_and_outlet_relationshipService.java @@ -0,0 +1,64 @@ +package com.ffii.fhsmsc.modules.Menu.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_and_outlet_relationship; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_and_outlet_relationshipResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_menu_and_outlet_relationshipReq; + +import jakarta.validation.Valid; +@Service +public class fb_menu_and_outlet_relationshipService extends AbstractBaseEntityService { + public fb_menu_and_outlet_relationshipService(JdbcDao jdbcDao, fb_menu_and_outlet_relationshipResponsity repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `fb_menu_and_outlet_relationship` fi" + ); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND fi.user_id = :userId"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_menu_and_outlet_relationship saveOrUpdate(@Valid fb_menu_and_outlet_relationshipReq req) { + fb_menu_and_outlet_relationship instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_menu_and_outlet_relationship(); + } + } else { + instance = new fb_menu_and_outlet_relationship(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_ingredientService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_ingredientService.java new file mode 100644 index 0000000..fd42abd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_ingredientService.java @@ -0,0 +1,63 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_item_ingredient; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_item_ingredientResponsity; +import com.ffii.fhsmsc.modules.Menu.req.fb_menu_item_ingredientReq; + +import jakarta.validation.Valid; +@Service + public class fb_menu_item_ingredientService extends AbstractBaseEntityService { + public fb_menu_item_ingredientService(JdbcDao jdbcDao, fb_menu_item_ingredientResponsity repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `fb_menu_item_ingredient` fi" + ); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND fi.user_id = :userId"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_menu_item_ingredient saveOrUpdate(@Valid fb_menu_item_ingredientReq req) { + fb_menu_item_ingredient instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_menu_item_ingredient(); + } + } else { + instance = new fb_menu_item_ingredient(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_nutritionService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_nutritionService.java new file mode 100644 index 0000000..e7cd82d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/fb_menu_item_nutritionService.java @@ -0,0 +1,63 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_item_nutrition; +import com.ffii.fhsmsc.modules.Menu.enity.fb_menu_item_nutritionResponsity; +import com.ffii.fhsmsc.modules.Menu.req.FbMenuItemNutrientReq; +import jakarta.validation.Valid; +@Service + public class fb_menu_item_nutritionService extends AbstractBaseEntityService { + public fb_menu_item_nutritionService(JdbcDao jdbcDao, fb_menu_item_nutritionResponsity repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `fb_menu_item_nutrition` fi" + ); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND fi.user_id = :userId"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_menu_item_nutrition saveOrUpdate(@Valid FbMenuItemNutrientReq req) { + fb_menu_item_nutrition instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_menu_item_nutrition(); + } + } else { + instance = new fb_menu_item_nutrition(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/service/ingredientService.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/ingredientService.java new file mode 100644 index 0000000..0208103 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/service/ingredientService.java @@ -0,0 +1,138 @@ +package com.ffii.fhsmsc.modules.Menu.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.I18n.req.i18nReq; +import com.ffii.fhsmsc.modules.I18n.service.i18nService; +import com.ffii.fhsmsc.modules.Menu.enity.ingredient; +import com.ffii.fhsmsc.modules.Menu.enity.ingredientResponsity; +import com.ffii.fhsmsc.modules.Menu.req.ingredientReq; + +import jakarta.validation.Valid; +@Service + public class ingredientService extends AbstractBaseEntityService { + private final i18nService i18nService; + private static final Logger logger = LoggerFactory.getLogger(ingredientService.class); + public ingredientService(JdbcDao jdbcDao, ingredientResponsity repository, i18nService i18nService) { + super(jdbcDao, repository); + this.i18nService = i18nService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder( + "SELECT i.Ingredient_id, i.Ingredient_Name, i.Allergy_Substance_ID, " + // 只选择需要的字段 + "i_en.value as name_en, " + + "i_zh.value as name_zh " + + "FROM `ingredient` i " + + "LEFT JOIN i18n i_en ON i.Ingredient_id = i_en.record_id " + + "AND i_en.table_name = 'Ingredient' " + + "AND i_en.field_name = 'Ingredient_Name' " + + "AND i_en.language = 'en' " + + "LEFT JOIN i18n i_zh ON i.Ingredient_id = i_zh.record_id " + + "AND i_zh.table_name = 'Ingredient' " + + "AND i_zh.field_name = 'Ingredient_Name' " + + "AND i_zh.language = 'zh' "); + + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND i.user_id = :userId"); + } + } + sql.append(" ORDER BY i.Ingredient_id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public ingredient saveOrUpdate(@Valid ingredientReq req) { + logger.info("Request: Ingredient_id={}, Ingredient_Name={}", + req.getIngredient_id(), req.getIngredient_Name()); + + ingredient instance; + boolean isNew = false; + + if (req.getIngredient_id() != null && req.getIngredient_id() > 0) { + instance = find(req.getIngredient_id()).orElseThrow(() -> + new InternalServerErrorException("Ingredient not found for id: " + req.getIngredient_id())); + logger.info("Found existing ingredient: id={}, Ingredient_Name={}", + instance.getId(), instance.getIngredient_Name()); + } else { + instance = new ingredient(); + isNew = true; + logger.info("Creating new ingredient"); + } + + // 设置基本字段 + instance.setIngredient_id(req.getIngredient_id()); + instance.setIngredient_Name(req.getIngredient_Name()); + + // 设置时间戳 + if (isNew) { + instance.setCreated(LocalDateTime.now()); + } + instance.setModified(LocalDateTime.now()); + + try { + instance = repository.saveAndFlush(instance); + logger.info("After save - Saved ingredient: id={}, Ingredient_Name={}", + instance.getId(), instance.getIngredient_Name()); + } catch (Exception e) { + logger.error("Failed to save ingredient: id={}, error={}", + req.getIngredient_id(), e.getMessage(), e); + throw new InternalServerErrorException("Failed to save ingredient: " + e.getMessage()); + } + + return instance; + } + + private void updateI18nEntry(String tableName, String fieldName, Long recordId, String language, String value) { + if (value != null) { + logger.info("Updating i18n entry: tableName={}, fieldName={}, recordId={}, language={}, value={}", + tableName, fieldName, recordId, language, value); + + // 先检查是否存在记录 + Map checkArgs = new HashMap<>(); + checkArgs.put("table_name", tableName); + checkArgs.put("field_name", fieldName); + checkArgs.put("record_id", recordId); + checkArgs.put("language", language); + + List> existingEntries = i18nService.search(checkArgs); + logger.info("Found {} existing entries", existingEntries.size()); + + if (!existingEntries.isEmpty()) { + // 如果存在记录,更新它 + Map existingEntry = existingEntries.get(0); + Long id = ((Number) existingEntry.get("id")).longValue(); + logger.info("Updating existing i18n entry with id={}", id); + i18nService.updateValue(tableName, fieldName, recordId, language, value); + } else { + // 如果不存在记录,创建新记录 + logger.info("Creating new i18n entry"); + i18nReq i18nReq = new i18nReq(); + i18nReq.setTable_name(tableName); + i18nReq.setField_name(fieldName); + i18nReq.setRecord_id(recordId); + i18nReq.setLanguage(language); + i18nReq.setValue(value); + + i18nService.saveOrUpdate(i18nReq); + } + } else { + logger.warn("Skipping i18n update for null value: tableName={}, fieldName={}, recordId={}, language={}", + tableName, fieldName, recordId, language); + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/FbMenuCombineController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/FbMenuCombineController.java new file mode 100644 index 0000000..6f9409f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/FbMenuCombineController.java @@ -0,0 +1,137 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.List; +import java.util.Map; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.DataRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.Menu.service.FbMenuCombineService; +@RestController +@RequestMapping("/menu") +public class FbMenuCombineController { + private final FbMenuCombineService fbMenuCombineService; + private final JdbcDao jdbcDao; + public FbMenuCombineController(FbMenuCombineService fbMenuCombineService, JdbcDao jdbcDao) { + this.fbMenuCombineService = fbMenuCombineService; + this.jdbcDao = jdbcDao; + } + + /** + * 获取完整的菜单信息,包括营养和配料 + */ + @GetMapping("/complete/{menuId}") + public DataRes> getCompleteMenu(@PathVariable Long menuId) { + return new DataRes<>(fbMenuCombineService.getCompleteMenu(menuId)); + } + + /** + * 获取特定门店的所有菜单 + */ + @GetMapping("/outlet/{outletId}") + public RecordsRes> getOutletMenus(@PathVariable Long outletId) { + return new RecordsRes<>(fbMenuCombineService.getOutletMenus(outletId)); + } + @PostMapping("/save") + public DataRes saveMenu(@RequestBody Map menuData) { + fbMenuCombineService.saveCompleteMenu(menuData); + return new DataRes<>(); + } + /** + * 批量获取多个菜单的完整信息 + */ + @GetMapping("/batch") + public RecordsRes> getBatchMenus(@RequestParam List menuIds) { + return new RecordsRes<>(fbMenuCombineService.getBatchMenus(menuIds)); + } + + + @PostMapping("/complete") + public DataRes saveCompleteMenu(@RequestBody Map menuData) { + fbMenuCombineService.saveCompleteMenu(menuData); + return new DataRes<>(); + } + @GetMapping("/fb/{fbId}") + public RecordsRes> getFbMenus(@PathVariable Long fbId) { + return new RecordsRes<>(fbMenuCombineService.getFbMenus(fbId)); + } + + @GetMapping("/operators-with-outlets-and-menus") + public RecordsRes> getOperatorsWithOutletsAndMenus() { + return new RecordsRes<>(fbMenuCombineService.getOperatorsWithOutletsAndMenus()); + } + + @PostMapping("/menu-outlet-relationships/batch-update") + public DataRes batchUpdateMenuOutletRelationships(@RequestBody Map data) { + fbMenuCombineService.saveMenuOutletRelationshipsByMenu(data); + return new DataRes<>(); + } + /** + * 删除菜品和门店的关联 + */ + @DeleteMapping("/outlet-relationship/delete/{menuId}/{outletId}") + public DataRes deleteMenuOutletRelationship( + @PathVariable Long menuId, + @PathVariable Long outletId) { + fbMenuCombineService.deleteMenuOutletRelationship(menuId, outletId); + return new DataRes<>(); + } + @DeleteMapping("/delete/{menuId}") + public DataRes deleteMenu(@PathVariable Long menuId) { + fbMenuCombineService.deleteMenu(menuId); + return new DataRes<>(); + } + + @GetMapping("/list-with-details") + public RecordsRes> getMenuListWithDetails() { + return new RecordsRes<>(fbMenuCombineService.getMenuListWithDetails()); + } + @PostMapping("/save-with-details") + public DataRes saveMenuWithDetails(@RequestBody Map menuData) { + fbMenuCombineService.saveMenuWithDetails(menuData); + return new DataRes<>(); + } + @PostMapping("/group-relationships/save") + public DataRes saveGroupRelationships(@RequestBody Map data) { + fbMenuCombineService.saveGroupRelationships(data); + return new DataRes<>(); + } + // FbMenuCombineController.java + + @PostMapping("/group-show-control/save") + public DataRes saveGroupShowControl(@RequestBody Map data) { + Long brandId = ((Number) data.get("FB_brand_id")).longValue(); + List> groupShowList = (List>) data.get("groupShowList"); + fbMenuCombineService.saveGroupShowControl(brandId, groupShowList); + return new DataRes<>(); + } + + @GetMapping("/group-show-control/list") + public RecordsRes> getGroupShowControlWithMenus( + @RequestParam Long FB_brand_id, + @RequestParam(required = false) Boolean showTable) { + return new RecordsRes<>(fbMenuCombineService.getGroupShowControlWithMenus(FB_brand_id, showTable)); + } + + // FbMenuCombineController.java + @GetMapping("/brand-menus") + public RecordsRes> getBrandMenus(@RequestParam Long FB_id) { + // 只返回菜单精简信息 + List> menus = fbMenuCombineService.getBrandMenusSimple(FB_id); + return new RecordsRes<>(menus); + } + + @PostMapping("/outlet-relationships/batch-update") + public DataRes batchUpdateOutletRelationships(@RequestBody Map data) { + fbMenuCombineService.saveMenuOutletRelationshipsByBatch(data); + return new DataRes<>(); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_groupController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_groupController.java new file mode 100644 index 0000000..a5ddea9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_groupController.java @@ -0,0 +1,61 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_groupReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_groupService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_group") +public class fb_groupController { + private fb_groupService fb_groupService; + private static final Logger logger = LoggerFactory.getLogger(fb_groupController.class); + public fb_groupController( + fb_groupService fb_groupService + ) { + this.fb_groupService = fb_groupService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_groupService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("FB_brand_id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_groupReq req) { + try { + logger.info("Received save request: {}", req); + IdRes result = new IdRes(fb_groupService.saveOrUpdate(req).getId()); + logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + logger.error("Error processing save request", e); + throw e; + } + } + @DeleteMapping("/delete/{id}") + public void deleteGroup(@PathVariable Long id) { + fb_groupService.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_relationshipController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_relationshipController.java new file mode 100644 index 0000000..e43e74b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_relationshipController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_group_relationshipReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_group_relationshipService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_group_relationship") +public class fb_group_relationshipController { + private fb_group_relationshipService fb_group_relationshipService; + + public fb_group_relationshipController( + fb_group_relationshipService fb_group_relationshipService + ) { + this.fb_group_relationshipService = fb_group_relationshipService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_group_relationshipService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("FB_brand_id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_group_relationshipReq req) { + return new IdRes(fb_group_relationshipService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_show_controlController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_show_controlController.java new file mode 100644 index 0000000..46da154 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_group_show_controlController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_group_show_controlReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_group_show_controlService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_group_show_control") +public class fb_group_show_controlController { + private fb_group_show_controlService fb_group_show_controlService; + + public fb_group_show_controlController( + fb_group_show_controlService fb_group_show_controlService + ) { + this.fb_group_show_controlService = fb_group_show_controlService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_group_show_controlService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_group_show_controlReq req) { + return new IdRes(fb_group_show_controlService.saveOrUpdate(req).getId()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menuController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menuController.java new file mode 100644 index 0000000..13ef3f3 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menuController.java @@ -0,0 +1,57 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_menuReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_menuService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_menu") +public class fb_menuController { + private fb_menuService fb_menuService; + private static final Logger logger = LoggerFactory.getLogger(fb_menuController.class); + public fb_menuController( + fb_menuService fb_menuService + ) { + this.fb_menuService = fb_menuService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_menuService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_menuReq req) { + try { + logger.info("Received save request: {}", req); + IdRes result = new IdRes(fb_menuService.saveOrUpdate(req).getId()); + logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + logger.error("Error processing save request", e); + throw e; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_and_outlet_relationshipController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_and_outlet_relationshipController.java new file mode 100644 index 0000000..76c47ef --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_and_outlet_relationshipController.java @@ -0,0 +1,47 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_menu_and_outlet_relationshipReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_menu_and_outlet_relationshipService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_menu_and_outlet_relationship") +public class fb_menu_and_outlet_relationshipController { + private fb_menu_and_outlet_relationshipService fb_menu_and_outlet_relationshipService; + + public fb_menu_and_outlet_relationshipController( + fb_menu_and_outlet_relationshipService fb_menu_and_outlet_relationshipService + ) { + this.fb_menu_and_outlet_relationshipService = fb_menu_and_outlet_relationshipService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_menu_and_outlet_relationshipService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_menu_and_outlet_relationshipReq req) { + return new IdRes(fb_menu_and_outlet_relationshipService.saveOrUpdate(req).getId()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_ingredientController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_ingredientController.java new file mode 100644 index 0000000..9990996 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_ingredientController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.fb_menu_item_ingredientReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_menu_item_ingredientService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_menu_item_ingredient") +public class fb_menu_item_ingredientController { + private fb_menu_item_ingredientService fb_menu_item_ingredientService; + + public fb_menu_item_ingredientController( + fb_menu_item_ingredientService fb_menu_item_ingredientService + ) { + this.fb_menu_item_ingredientService = fb_menu_item_ingredientService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_menu_item_ingredientService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_menu_item_ingredientReq req) { + return new IdRes(fb_menu_item_ingredientService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_nutritionController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_nutritionController.java new file mode 100644 index 0000000..8fa7e94 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/fb_menu_item_nutritionController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.FbMenuItemNutrientReq; +import com.ffii.fhsmsc.modules.Menu.service.fb_menu_item_nutritionService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_menu_item_nutrition") +public class fb_menu_item_nutritionController { + private fb_menu_item_nutritionService fb_menu_item_nutritionService; + + public fb_menu_item_nutritionController( + fb_menu_item_nutritionService fb_menu_item_nutritionService + ) { + this.fb_menu_item_nutritionService = fb_menu_item_nutritionService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_menu_item_nutritionService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid FbMenuItemNutrientReq req) { + return new IdRes(fb_menu_item_nutritionService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/Menu/web/ingredientController.java b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/ingredientController.java new file mode 100644 index 0000000..8fe11b6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/Menu/web/ingredientController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.Menu.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.Menu.req.ingredientReq; +import com.ffii.fhsmsc.modules.Menu.service.ingredientService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/ingredient") +public class ingredientController { + private ingredientService ingredientService; + + public ingredientController( + ingredientService ingredientService + ) { + this.ingredientService = ingredientService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(ingredientService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid ingredientReq req) { + return new IdRes(ingredientService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipRules.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipRules.java new file mode 100644 index 0000000..4584a37 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipRules.java @@ -0,0 +1,46 @@ +package com.ffii.fhsmsc.modules.User_group.constant; + +import java.util.Map; +import java.util.Set; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Collections; + +public class RelationshipRules { + private static final Map> ALLOWED_OPERATIONS; + + static { + Map> map = new HashMap<>(); + + // Admin permissions + Set adminOps = new HashSet<>(); + adminOps.add("ADD_MEMBER"); + adminOps.add("REMOVE_MEMBER"); + adminOps.add("UPDATE_MEMBER"); + adminOps.add("ADD_ADMIN"); + map.put(RelationshipTypes.ADMIN, Collections.unmodifiableSet(adminOps)); + + // Member permissions + Set memberOps = new HashSet<>(); + memberOps.add("VIEW_MEMBER"); + map.put(RelationshipTypes.MEMBER, Collections.unmodifiableSet(memberOps)); + + // Delegate permissions + Set delegateOps = new HashSet<>(); + delegateOps.add("ADD_MEMBER"); + delegateOps.add("REMOVE_MEMBER"); + delegateOps.add("UPDATE_MEMBER"); + map.put(RelationshipTypes.DELEGATE, Collections.unmodifiableSet(delegateOps)); + + ALLOWED_OPERATIONS = Collections.unmodifiableMap(map); + } + + public static boolean canPerformOperation(String relationshipType, String operation) { + Set allowedOps = ALLOWED_OPERATIONS.get(relationshipType); + return allowedOps != null && allowedOps.contains(operation); + } + + private RelationshipRules() { + // Private constructor to prevent instantiation + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipTypes.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipTypes.java new file mode 100644 index 0000000..51e8609 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/constant/RelationshipTypes.java @@ -0,0 +1,11 @@ +package com.ffii.fhsmsc.modules.User_group.constant; + +public class RelationshipTypes { + public static final String ADMIN = "ADMIN"; + public static final String MEMBER = "MEMBER"; + public static final String DELEGATE = "DELEGATE"; + + private RelationshipTypes() { + // Private constructor to prevent instantiation + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ new file mode 100644 index 0000000..d98a001 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/.LCKUserGroupMembersRepository.java~ @@ -0,0 +1 @@ +C:\workspace\FHSMS-C-backend\src\main\java\com\ffii\fhsmsc\modules\User_group\enity\UserGroupMembersRepository.java \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserDelegation.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserDelegation.java new file mode 100644 index 0000000..ec10395 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserDelegation.java @@ -0,0 +1,75 @@ +package com.ffii.fhsmsc.modules.User_group.enity; + +import java.util.Date; + +import com.ffii.core.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "User_Delegation") +public class UserDelegation extends BaseEntity { + @Column + private Long User_ID; + @Column + private Long Delegate_To_User_ID; + @Column + private Long Data_Group_ID; + @Column + private Date Delegation_Start_Date; + @Column + private Date Delegation_Expiry_Date; + @Column + private Date Creation_Date_Time; + + public Long getUser_ID() { + return User_ID; + } + + public void setUser_ID(Long user_ID) { + User_ID = user_ID; + } + + public Long getDelegate_To_User_ID() { + return Delegate_To_User_ID; + } + + public void setDelegate_To_User_ID(Long delegate_To_User_ID) { + Delegate_To_User_ID = delegate_To_User_ID; + } + + public Long getData_Group_ID() { + return Data_Group_ID; + } + + public void setData_Group_ID(Long data_Group_ID) { + Data_Group_ID = data_Group_ID; + } + + public Date getDelegation_Start_Date() { + return Delegation_Start_Date; + } + + public void setDelegation_Start_Date(Date delegation_Start_Date) { + Delegation_Start_Date = delegation_Start_Date; + } + + public Date getDelegation_Expiry_Date() { + return Delegation_Expiry_Date; + } + + public void setDelegation_Expiry_Date(Date delegation_Expiry_Date) { + Delegation_Expiry_Date = delegation_Expiry_Date; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupAdmin.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupAdmin.java new file mode 100644 index 0000000..93737c9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupAdmin.java @@ -0,0 +1,54 @@ +package com.ffii.fhsmsc.modules.User_group.enity; + +import java.util.Date; + +import com.ffii.core.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "User_Group_Admin") +public class UserGroupAdmin extends BaseEntity { + @Column + private Long User_Grp_Org_ID; + @Column + private Long Admin_User_ID; + @Column + private String Organization_Role; + @Column + private Date Creation_Date_Time; + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + + public Long getAdmin_User_ID() { + return Admin_User_ID; + } + + public void setAdmin_User_ID(Long admin_User_ID) { + Admin_User_ID = admin_User_ID; + } + + public String getOrganization_Role() { + return Organization_Role; + } + + public void setOrganization_Role(String organization_Role) { + Organization_Role = organization_Role; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupMembers.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupMembers.java new file mode 100644 index 0000000..d68361d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupMembers.java @@ -0,0 +1,79 @@ +package com.ffii.fhsmsc.modules.User_group.enity; +import com.ffii.core.entity.BaseEntity; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; + +/** @author Terence */ +@Entity +@Table(name = "User_Group_Members", + uniqueConstraints = { + @UniqueConstraint(columnNames = {"User_Grp_Org_ID", "User_ID"}) + } +) +public class UserGroupMembers extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long User_Grp_Org_ID; + + @Column(nullable = false) + private Long User_ID; + + @Column + private String R_Ship_Creator; + + @Column + private Date Creation_Date_Time; + + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + + public Long getUser_ID() { + return User_ID; + } + + public void setUser_ID(Long user_ID) { + User_ID = user_ID; + } + + public String getR_Ship_Creator() { + return R_Ship_Creator; + } + + public void setR_Ship_Creator(String r_Ship_Creator) { + R_Ship_Creator = r_Ship_Creator; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupOrganization.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupOrganization.java new file mode 100644 index 0000000..ce57488 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/UserGroupOrganization.java @@ -0,0 +1,54 @@ +package com.ffii.fhsmsc.modules.User_group.enity; +import com.ffii.core.entity.BaseEntity; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "User_Group_Organization") +public class UserGroupOrganization extends BaseEntity { + @Column + private Long User_Grp_Org_ID; + @Column + private String User_Grp_Org_Name; + @Column + private String Creator_User_ID; + @Column + private Date Creation_Date_Time; + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + + public String getUser_Grp_Org_Name() { + return User_Grp_Org_Name; + } + + public void setUser_Grp_Org_Name(String user_Grp_Org_Name) { + User_Grp_Org_Name = user_Grp_Org_Name; + } + + public String getCreator_User_ID() { + return Creator_User_ID; + } + + public void setCreator_User_ID(String creator_User_ID) { + Creator_User_ID = creator_User_ID; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/User_authority.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/User_authority.java new file mode 100644 index 0000000..5d0785f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/User_authority.java @@ -0,0 +1,33 @@ +package com.ffii.fhsmsc.modules.User_group.enity; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "user_authority") +public class User_authority extends BaseEntity { + @Column(nullable = false) + private Long userId; + @Column(nullable = false) + private Long authId; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getAuthId() { + return authId; + } + + public void setAuthId(Long authId) { + this.authId = authId; + } +} + \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/authority.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/authority.java new file mode 100644 index 0000000..bace2b1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/enity/authority.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.User_group.enity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "authority") +public class authority extends BaseEntity { + @Column + private Long id; + @Column + private String authority; + @Column + private String name; + + public void setId(Long id) { + this.id = id; + } + public String getAuthority() { + return authority; + } + public void setAuthority(String authority) { + this.authority = authority; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + + + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserDelegationRepository.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserDelegationRepository.java new file mode 100644 index 0000000..50fbb54 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserDelegationRepository.java @@ -0,0 +1,9 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.UserDelegation; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserDelegationRepository extends AbstractRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupAdminRepository.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupAdminRepository.java new file mode 100644 index 0000000..86e60ea --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupAdminRepository.java @@ -0,0 +1,9 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupAdmin; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserGroupAdminRepository extends AbstractRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupMembersRepository.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupMembersRepository.java new file mode 100644 index 0000000..ac7e193 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupMembersRepository.java @@ -0,0 +1,38 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupMembers; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.jpa.repository.Modifying; +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserGroupMembersRepository extends AbstractRepository { + + @Query("SELECT m FROM UserGroupMembers m WHERE m.User_Grp_Org_ID = :orgId") + List findByUserGrpOrgId(@Param("orgId") Long orgId); + + @Query("SELECT m FROM UserGroupMembers m WHERE m.User_Grp_Org_ID = :orgId AND m.R_Ship_Creator = :relationshipType") + List findByUserGrpOrgIdAndRelationshipType( + @Param("orgId") Long orgId, + @Param("relationshipType") String relationshipType + ); + + @Query("SELECT m FROM UserGroupMembers m WHERE m.User_Grp_Org_ID = :orgId AND m.User_ID = :userId") + Optional findByUserGrpOrgIdAndUserId( + @Param("orgId") Long orgId, + @Param("userId") Long userId + ); + + @Modifying + @Transactional + @Query("DELETE FROM UserGroupMembers m WHERE m.User_Grp_Org_ID = :orgId AND m.User_ID = :userId") + void deleteByUserGrpOrgIdAndUserId( + @Param("orgId") Long orgId, + @Param("userId") Long userId + ); +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupOrganizationRepository.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupOrganizationRepository.java new file mode 100644 index 0000000..80cf3e3 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/UserGroupOrganizationRepository.java @@ -0,0 +1,9 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupOrganization; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserGroupOrganizationRepository extends AbstractRepository { +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/User_authorityRepository.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/User_authorityRepository.java new file mode 100644 index 0000000..5464e3a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/User_authorityRepository.java @@ -0,0 +1,38 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.User_authority; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.jpa.repository.Modifying; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface User_authorityRepository extends AbstractRepository { + + @Query("SELECT ua FROM User_authority ua WHERE ua.userId = :userId") + List findByUserId(@Param("userId") Long userId); + + @Query("SELECT ua FROM User_authority ua WHERE ua.userId = :userId AND ua.authId = :authId") + Optional findByUserIdAndAuthId( + @Param("userId") Long userId, + @Param("authId") Long authId + ); + + @Modifying + @Transactional + @Query("DELETE FROM User_authority ua WHERE ua.userId = :userId") + void deleteByUserId(@Param("userId") Long userId); + + @Modifying + @Transactional + @Query("DELETE FROM User_authority ua WHERE ua.userId = :userId AND ua.authId = :authId") + void deleteByUserIdAndAuthId( + @Param("userId") Long userId, + @Param("authId") Long authId + ); +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/authorityReponsitory.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/authorityReponsitory.java new file mode 100644 index 0000000..95e7d6b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/repository/authorityReponsitory.java @@ -0,0 +1,23 @@ +package com.ffii.fhsmsc.modules.User_group.repository; + +import com.ffii.core.support.AbstractRepository; +import com.ffii.fhsmsc.modules.User_group.enity.authority; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface authorityReponsitory extends AbstractRepository { + + @Query("SELECT a FROM authority a WHERE a.authority = :authority") + Optional findByAuthority(@Param("authority") String authority); + + @Query("SELECT a FROM authority a WHERE a.name = :name") + Optional findByName(@Param("name") String name); + + @Query("SELECT a FROM authority a ORDER BY a.id") + List findAllOrderedById(); +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/DeleteMemberWithAuthoritiesReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/DeleteMemberWithAuthoritiesReq.java new file mode 100644 index 0000000..454d7f1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/DeleteMemberWithAuthoritiesReq.java @@ -0,0 +1,34 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.List; + +public class DeleteMemberWithAuthoritiesReq { + private Long orgId; + private Long userId; + private List authorityIds; // 要删除的权限ID列表,如果为空则删除所有权限 + + // Getters and setters + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public List getAuthorityIds() { + return authorityIds; + } + + public void setAuthorityIds(List authorityIds) { + this.authorityIds = authorityIds; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/MemberUpdateReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/MemberUpdateReq.java new file mode 100644 index 0000000..4d06339 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/MemberUpdateReq.java @@ -0,0 +1,43 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.List; + +public class MemberUpdateReq { + private Long orgId; + private String relationshipType; + private List authorityIds; + private boolean replaceExisting = false; + + // Getters and setters + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public String getRelationshipType() { + return relationshipType; + } + + public void setRelationshipType(String relationshipType) { + this.relationshipType = relationshipType; + } + + public List getAuthorityIds() { + return authorityIds; + } + + public void setAuthorityIds(List authorityIds) { + this.authorityIds = authorityIds; + } + + public boolean isReplaceExisting() { + return replaceExisting; + } + + public void setReplaceExisting(boolean replaceExisting) { + this.replaceExisting = replaceExisting; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserCombineReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserCombineReq.java new file mode 100644 index 0000000..7eaa078 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserCombineReq.java @@ -0,0 +1,62 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserCombineReq { + + + @JsonProperty("User_Grp_Org_ID") + private Long User_Grp_Org_ID; + + @JsonProperty("User_ID") + private Long User_ID; + @JsonProperty("Admin_User_ID") + private Long Admin_User_ID; + @JsonProperty("User_Grp_Org_Name") + private String User_Grp_Org_Name; + @JsonProperty("R_Ship_Creator") + private String R_Ship_Creator; + + + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } + + public Long getUser_ID() { + return User_ID; + } + + public void setUser_ID(Long User_ID) { + this.User_ID = User_ID; + } + + public Long getAdmin_User_ID() { + return Admin_User_ID; + } + + public void setAdmin_User_ID(Long Admin_User_ID) { + this.Admin_User_ID = Admin_User_ID; + } + + public String getUser_Grp_Org_Name() { + return User_Grp_Org_Name; + } + + public void setUser_Grp_Org_Name(String User_Grp_Org_Name) { + this.User_Grp_Org_Name = User_Grp_Org_Name; + } + + public String getR_Ship_Creator() { + return R_Ship_Creator; + } + + public void setR_Ship_Creator(String R_Ship_Creator) { + this.R_Ship_Creator = R_Ship_Creator; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserDelegationReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserDelegationReq.java new file mode 100644 index 0000000..644480f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserDelegationReq.java @@ -0,0 +1,60 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.Date; + +public class UserDelegationReq { + private Long User_ID; + private Long Delegate_To_User_ID; + private Long Data_Group_ID; + private Date Delegation_Start_Date; + private Date Delegation_Expiry_Date; + private Date Creation_Date_Time; + + public Long getUser_ID() { + return User_ID; + } + + public void setUser_ID(Long user_ID) { + User_ID = user_ID; + } + + public Long getDelegate_To_User_ID() { + return Delegate_To_User_ID; + } + + public void setDelegate_To_User_ID(Long delegate_To_User_ID) { + Delegate_To_User_ID = delegate_To_User_ID; + } + + public Long getData_Group_ID() { + return Data_Group_ID; + } + + public void setData_Group_ID(Long data_Group_ID) { + Data_Group_ID = data_Group_ID; + } + + public Date getDelegation_Start_Date() { + return Delegation_Start_Date; + } + + public void setDelegation_Start_Date(Date delegation_Start_Date) { + Delegation_Start_Date = delegation_Start_Date; + } + + public Date getDelegation_Expiry_Date() { + return Delegation_Expiry_Date; + } + + public void setDelegation_Expiry_Date(Date delegation_Expiry_Date) { + Delegation_Expiry_Date = delegation_Expiry_Date; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupAdminReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupAdminReq.java new file mode 100644 index 0000000..871f9f6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupAdminReq.java @@ -0,0 +1,42 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.Date; + +public class UserGroupAdminReq { + private Long User_Grp_Org_ID; + private Long Admin_User_ID; + private String Organization_Role; + private Date Creation_Date_Time; + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + + public Long getAdmin_User_ID() { + return Admin_User_ID; + } + + public void setAdmin_User_ID(Long admin_User_ID) { + Admin_User_ID = admin_User_ID; + } + + public String getOrganization_Role() { + return Organization_Role; + } + + public void setOrganization_Role(String organization_Role) { + Organization_Role = organization_Role; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMemberBatchReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMemberBatchReq.java new file mode 100644 index 0000000..b89bdbe --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMemberBatchReq.java @@ -0,0 +1,66 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.List; + +public class UserGroupMemberBatchReq { + private Long orgId; + private String relationshipType; + private List members; + + // Inner class for member with authorities + public static class MemberWithAuthorities { + private Long userId; + private List authorityIds; + private String relationshipType; + + // Getters and setters + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public List getAuthorityIds() { + return authorityIds; + } + + public void setAuthorityIds(List authorityIds) { + this.authorityIds = authorityIds; + } + + public String getRelationshipType() { + return relationshipType; + } + + public void setRelationshipType(String relationshipType) { + this.relationshipType = relationshipType; + } + } + + // Getters and setters for main class + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public String getRelationshipType() { + return relationshipType; + } + + public void setRelationshipType(String relationshipType) { + this.relationshipType = relationshipType; + } + + public List getMembers() { + return members; + } + + public void setMembers(List members) { + this.members = members; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMembersReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMembersReq.java new file mode 100644 index 0000000..8cb6e5a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupMembersReq.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.Date; + +public class UserGroupMembersReq { + private Long User_Grp_Org_ID; + private Long User_ID; + private String R_ship_Creator; + private Date Creation_Date_Time; + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + public Long getUser_ID() { + return User_ID; + } + + public void setUser_ID(Long user_ID) { + User_ID = user_ID; + } + + public String getR_ship_Creator() { + return R_ship_Creator; + } + + public void setR_ship_Creator(String r_ship_Creator) { + R_ship_Creator = r_ship_Creator; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupOrganizationReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupOrganizationReq.java new file mode 100644 index 0000000..10d4a37 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/UserGroupOrganizationReq.java @@ -0,0 +1,44 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +import java.util.Date; + +public class UserGroupOrganizationReq { + private Long User_Grp_Org_ID; + private String User_Grp_Org_Name; + private long Creator_User_ID; + private Date Creation_Date_Time; + + public Long getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setUser_Grp_Org_ID(Long user_Grp_Org_ID) { + User_Grp_Org_ID = user_Grp_Org_ID; + } + + public String getUser_Grp_Org_Name() { + return User_Grp_Org_Name; + } + + public void setUser_Grp_Org_Name(String user_Grp_Org_Name) { + User_Grp_Org_Name = user_Grp_Org_Name; + } + + public long getCreator_User_ID() { + return Creator_User_ID; + } + + public void setCreator_User_ID(long creator_User_ID) { + Creator_User_ID = creator_User_ID; + } + + public Date getCreation_Date_Time() { + return Creation_Date_Time; + } + + public void setCreation_Date_Time(Date creation_Date_Time) { + Creation_Date_Time = creation_Date_Time; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/User_authorityReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/User_authorityReq.java new file mode 100644 index 0000000..51a4cf8 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/User_authorityReq.java @@ -0,0 +1,18 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +public class User_authorityReq { + private Long userId; + private Long auth_id; + public Long getuserId() { + return userId; + } + public void setuserId(Long userId) { + this.userId = userId; + } + public Long getAuth_id() { + return auth_id; + } + public void setAuth_id(Long auth_id) { + this.auth_id = auth_id; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/req/authorityReq.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/authorityReq.java new file mode 100644 index 0000000..a78b441 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/req/authorityReq.java @@ -0,0 +1,18 @@ +package com.ffii.fhsmsc.modules.User_group.req; + +public class authorityReq { + private String authority; + private String name; + public String getAuthority() { + return authority; + } + public void setAuthority(String authority) { + this.authority = authority; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserCombineService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserCombineService.java new file mode 100644 index 0000000..4ae243b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserCombineService.java @@ -0,0 +1,259 @@ +package com.ffii.fhsmsc.modules.User_group.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.User_group.req.DeleteMemberWithAuthoritiesReq; +import com.ffii.fhsmsc.modules.User_group.req.UserCombineReq; +@Service +public class UserCombineService { + private static final Logger logger = LoggerFactory.getLogger(UserCombineService.class); + + private final JdbcDao jdbcDao; + + @Autowired + public UserCombineService(JdbcDao jdbcDao) { + this.jdbcDao = jdbcDao; + } + + @Transactional(rollbackFor = Exception.class) + public Map saveUserGroup(UserCombineReq req) { + logger.info("Saving user group with request: {}", req); + + // 检查组织是否存在 + Map existingOrg = null; + if (req.getUser_Grp_Org_ID() != null) { + String checkSql = "SELECT * FROM user_group_organization WHERE User_Grp_Org_ID = :orgId"; + Map params = new HashMap<>(); + params.put("orgId", req.getUser_Grp_Org_ID()); + List> results = jdbcDao.queryForList(checkSql, params); + if (!results.isEmpty()) { + existingOrg = results.get(0); + } + } + + if (existingOrg != null) { + // 组织已存在,只添加成员记录 + String memberSql = "INSERT INTO user_group_members (User_Grp_Org_ID, User_ID, R_Ship_Creator, Creation_Date_Time) " + + "VALUES (:User_Grp_Org_ID, :User_ID, :R_Ship_Creator, NOW()) " + + "ON DUPLICATE KEY UPDATE R_Ship_Creator = :R_Ship_Creator"; + + Map memberParams = new HashMap<>(); + memberParams.put("User_Grp_Org_ID", req.getUser_Grp_Org_ID()); + memberParams.put("User_ID", req.getUser_ID()); + memberParams.put("R_Ship_Creator", req.getR_Ship_Creator()); + + jdbcDao.executeUpdate(memberSql, memberParams); + + // 如果提供了Admin_User_ID,也添加管理员记录 + if (req.getAdmin_User_ID() != null) { + String adminSql = "INSERT INTO user_group_admin (User_Grp_Org_ID, Admin_User_ID, Organization_Role, Creation_Date_Time) " + + "VALUES (:User_Grp_Org_ID, :Admin_User_ID, 'ADMIN', NOW()) " + + "ON DUPLICATE KEY UPDATE Organization_Role = 'ADMIN'"; + + Map adminParams = new HashMap<>(); + adminParams.put("User_Grp_Org_ID", req.getUser_Grp_Org_ID()); + adminParams.put("Admin_User_ID", req.getAdmin_User_ID()); + + jdbcDao.executeUpdate(adminSql, adminParams); + } + } else { + // 创建新组织 + String orgSql = "INSERT INTO user_group_organization (User_Grp_Org_Name, Creator_User_ID, Creation_Date_Time) " + + "VALUES (:User_Grp_Org_Name, :Creator_User_ID, NOW())"; + + Map orgParams = new HashMap<>(); + orgParams.put("User_Grp_Org_Name", req.getUser_Grp_Org_Name()); + orgParams.put("Creator_User_ID", req.getUser_ID()); + + Long newOrgId = jdbcDao.executeUpdateAndReturnId(orgSql, orgParams); + req.setUser_Grp_Org_ID(newOrgId); + + // 添加成员记录 + String memberSql = "INSERT INTO user_group_members (User_Grp_Org_ID, User_ID, R_Ship_Creator, Creation_Date_Time) " + + "VALUES (:User_Grp_Org_ID, :User_ID, :R_Ship_Creator, NOW())"; + + Map memberParams = new HashMap<>(); + memberParams.put("User_Grp_Org_ID", newOrgId); + memberParams.put("User_ID", req.getUser_ID()); + memberParams.put("R_Ship_Creator", req.getR_Ship_Creator()); + + jdbcDao.executeUpdate(memberSql, memberParams); + + // 如果提供了Admin_User_ID,添加管理员记录 + if (req.getAdmin_User_ID() != null) { + String adminSql = "INSERT INTO user_group_admin (User_Grp_Org_ID, Admin_User_ID, Organization_Role, Creation_Date_Time) " + + "VALUES (:User_Grp_Org_ID, :Admin_User_ID, 'ADMIN', NOW())"; + + Map adminParams = new HashMap<>(); + adminParams.put("User_Grp_Org_ID", newOrgId); + adminParams.put("Admin_User_ID", req.getAdmin_User_ID()); + + jdbcDao.executeUpdate(adminSql, adminParams); + } + } + + return getGroupDetail(req.getUser_Grp_Org_ID()); + } + + @Transactional(readOnly = true) + public List> getUserGroups(Long userId) { + String sql = "SELECT DISTINCT o.*, " + + "CASE WHEN a.Admin_User_ID IS NOT NULL THEN true ELSE false END as is_admin " + + "FROM user_group_organization o " + + "LEFT JOIN user_group_admin a ON o.User_Grp_Org_ID = a.User_Grp_Org_ID AND a.Admin_User_ID = :userId " + + "LEFT JOIN user_group_members m ON o.User_Grp_Org_ID = m.User_Grp_Org_ID AND m.User_ID = :userId " + + "WHERE a.Admin_User_ID IS NOT NULL OR m.User_ID IS NOT NULL"; + + Map params = new HashMap<>(); + params.put("userId", userId); + + return jdbcDao.queryForList(sql, params); + } + + @Transactional(readOnly = true) + public Map getGroupDetail(Long orgId) { + String sql = "SELECT o.*, " + + "GROUP_CONCAT(DISTINCT a.Admin_User_ID) as admin_ids, " + + "GROUP_CONCAT(DISTINCT m.User_ID) as member_ids " + + "FROM user_group_organization o " + + "LEFT JOIN user_group_admin a ON o.User_Grp_Org_ID = a.User_Grp_Org_ID " + + "LEFT JOIN user_group_members m ON o.User_Grp_Org_ID = m.User_Grp_Org_ID " + + "WHERE o.User_Grp_Org_ID = :orgId " + + "GROUP BY o.User_Grp_Org_ID"; + + Map params = new HashMap<>(); + params.put("orgId", orgId); + + List> results = jdbcDao.queryForList(sql, params); + return results.isEmpty() ? null : results.get(0); + } + + @Transactional(readOnly = true) + public List> getGroupMembers(Long orgId) { + String sql = "SELECT m.*, " + + "CASE WHEN a.Admin_User_ID IS NOT NULL THEN true ELSE false END as is_admin " + + "FROM user_group_members m " + + "LEFT JOIN user_group_admin a ON m.User_Grp_Org_ID = a.User_Grp_Org_ID " + + "AND m.User_ID = a.Admin_User_ID " + + "WHERE m.User_Grp_Org_ID = :orgId"; + + Map params = new HashMap<>(); + params.put("orgId", orgId); + + return jdbcDao.queryForList(sql, params); + } + + @Transactional(rollbackFor = Exception.class) + public void deleteUserGroup(Long orgId, Long adminUserId) { + // 验证是否为管理员 + String checkSql = "SELECT COUNT(*) FROM user_group_admin WHERE User_Grp_Org_ID = :orgId AND Admin_User_ID = :adminUserId"; + Map checkParams = new HashMap<>(); + checkParams.put("orgId", orgId); + checkParams.put("adminUserId", adminUserId); + + int count = jdbcDao.queryForInt(checkSql, checkParams); + if (count == 0) { + throw new RuntimeException("User is not an admin of this group"); + } + + // 删除成员记录 + String memberSql = "DELETE FROM user_group_members WHERE User_Grp_Org_ID = :orgId"; + Map memberParams = new HashMap<>(); + memberParams.put("orgId", orgId); + jdbcDao.executeUpdate(memberSql, memberParams); + + // 删除管理员记录 + String adminSql = "DELETE FROM user_group_admin WHERE User_Grp_Org_ID = :orgId"; + Map adminParams = new HashMap<>(); + adminParams.put("orgId", orgId); + jdbcDao.executeUpdate(adminSql, adminParams); + + // 删除组织记录 + String orgSql = "DELETE FROM user_group_organization WHERE User_Grp_Org_ID = :orgId"; + Map orgParams = new HashMap<>(); + orgParams.put("orgId", orgId); + jdbcDao.executeUpdate(orgSql, orgParams); + } + + @Transactional(rollbackFor = Exception.class) + public void removeGroupMember(Long orgId, Long userId) { + // 首先检查用户是否是该组织的成员 + String checkSql = "SELECT COUNT(*) FROM user_group_members WHERE User_Grp_Org_ID = :orgId AND User_ID = :userId"; + Map checkParams = new HashMap<>(); + checkParams.put("orgId", orgId); + checkParams.put("userId", userId); + + int count = jdbcDao.queryForInt(checkSql, checkParams); + if (count == 0) { + throw new RuntimeException("用户不是该组织的成员"); + } + + // 删除成员记录 + String deleteSql = "DELETE FROM user_group_members WHERE User_Grp_Org_ID = :orgId AND User_ID = :userId"; + Map deleteParams = new HashMap<>(); + deleteParams.put("orgId", orgId); + deleteParams.put("userId", userId); + + jdbcDao.executeUpdate(deleteSql, deleteParams); + } + + @Transactional(rollbackFor = Exception.class) + public void deleteMemberAndRelatedRecords(DeleteMemberWithAuthoritiesReq req) { + Long orgId = req.getOrgId(); + Long userId = req.getUserId(); + List authorityIds = req.getAuthorityIds(); + + // 1. 检查用户是否是该组织的成员 + String checkSql = "SELECT COUNT(*) FROM user_group_members WHERE User_Grp_Org_ID = :orgId AND User_ID = :userId"; + Map checkParams = new HashMap<>(); + checkParams.put("orgId", orgId); + checkParams.put("userId", userId); + + int count = jdbcDao.queryForInt(checkSql, checkParams); + if (count == 0) { + throw new RuntimeException("用户不是该组织的成员"); + } + + // 2. 删除成员记录 + String memberSql = "DELETE FROM user_group_members WHERE User_Grp_Org_ID = :orgId AND User_ID = :userId"; + Map memberParams = new HashMap<>(); + memberParams.put("orgId", orgId); + memberParams.put("userId", userId); + jdbcDao.executeUpdate(memberSql, memberParams); + + // 3. 删除管理员记录(如果存在) + String adminSql = "DELETE FROM user_group_admin WHERE User_Grp_Org_ID = :orgId AND Admin_User_ID = :userId"; + Map adminParams = new HashMap<>(); + adminParams.put("orgId", orgId); + adminParams.put("userId", userId); + jdbcDao.executeUpdate(adminSql, adminParams); + + // 4. 删除指定的用户权限记录 + if (authorityIds != null && !authorityIds.isEmpty()) { + // 删除指定的权限 + for (Long authId : authorityIds) { + String authoritySql = "DELETE FROM user_authority WHERE userId = :userId AND authId = :authId"; + Map authorityParams = new HashMap<>(); + authorityParams.put("userId", userId); + authorityParams.put("authId", authId); + jdbcDao.executeUpdate(authoritySql, authorityParams); + } + } else { + // 如果没有指定权限ID,则删除所有权限 + String authoritySql = "DELETE FROM user_authority WHERE userId = :userId"; + Map authorityParams = new HashMap<>(); + authorityParams.put("userId", userId); + jdbcDao.executeUpdate(authoritySql, authorityParams); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserDelegationService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserDelegationService.java new file mode 100644 index 0000000..8ac03b7 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserDelegationService.java @@ -0,0 +1,59 @@ +package com.ffii.fhsmsc.modules.User_group.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.UserDelegation; +import com.ffii.fhsmsc.modules.User_group.repository.UserDelegationRepository; +import com.ffii.fhsmsc.modules.User_group.req.UserDelegationReq; + +import jakarta.validation.Valid; + +@Service +public class UserDelegationService extends AbstractBaseEntityService { + public UserDelegationService(JdbcDao jdbcDao, UserDelegationRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM User_Delegation fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("User_ID")) sql.append(" AND fi.User_ID = :User_ID"); + if (args.containsKey("R_ship_Creator")) sql.append(" AND fi.R_ship_Creator = :R_ship_Creator"); + } + + sql.append(" ORDER BY fi.User_ID"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public UserDelegation saveOrUpdate(@Valid UserDelegationReq req) { + UserDelegation instance; + + if (req.getUser_ID() != null) { + if(req.getUser_ID()>0){ + instance = find(req.getUser_ID()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new UserDelegation(); + } + } else { + instance = new UserDelegation(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupAdminService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupAdminService.java new file mode 100644 index 0000000..774572b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupAdminService.java @@ -0,0 +1,61 @@ +package com.ffii.fhsmsc.modules.User_group.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupAdmin; +import com.ffii.fhsmsc.modules.User_group.repository.UserGroupAdminRepository; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupAdminReq; + +import jakarta.validation.Valid; + +@Service +public class UserGroupAdminService extends AbstractBaseEntityService { + public UserGroupAdminService(JdbcDao jdbcDao, UserGroupAdminRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM fb_outlets fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("FB_id")) sql.append(" AND fi.FB_id = :FB_id"); + if (args.containsKey("FB_outlet_id")) sql.append(" AND fi.FB_outlet_id = :FB_outlet_id"); + if (args.containsKey("FB_outlet_district")) sql.append(" AND fi.FB_outlet_district = :FB_outlet_district"); + } + + sql.append(" ORDER BY fi.FB_id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public UserGroupAdmin saveOrUpdate(@Valid UserGroupAdminReq req) { + UserGroupAdmin instance; + + if (req.getUser_Grp_Org_ID() != null) { + if(req.getUser_Grp_Org_ID()>0){ + instance = find(req.getUser_Grp_Org_ID()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new UserGroupAdmin(); + } + } else { + instance = new UserGroupAdmin(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupCombineService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupCombineService.java new file mode 100644 index 0000000..5efd67c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupCombineService.java @@ -0,0 +1,124 @@ +package com.ffii.fhsmsc.modules.User_group.service; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.sql.Timestamp; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.User_group.req.*; +import com.ffii.fhsmsc.modules.User_group.constant.*; +import com.ffii.fhsmsc.modules.User_group.enity.*; +import java.util.Optional; +import java.util.Date; +import com.ffii.fhsmsc.modules.User_group.repository.UserGroupMembersRepository; +import org.springframework.beans.factory.annotation.Autowired; +import com.ffii.fhsmsc.modules.User_group.repository.User_authorityRepository; + +@Service +public class UserGroupCombineService { + @Autowired + private UserGroupMembersRepository userGroupMembersRepository; + @Autowired + private User_authorityRepository userAuthorityRepository; + @Autowired + private JdbcDao jdbcDao; + + public List getGroupMembersWithAuthorities(Long orgId, String relationshipType) { + if (relationshipType != null && !relationshipType.isEmpty()) { + return userGroupMembersRepository.findByUserGrpOrgIdAndRelationshipType(orgId, relationshipType); + } + return userGroupMembersRepository.findByUserGrpOrgId(orgId); + } + + @Transactional + public void addMemberWithAuthorities(UserGroupMemberBatchReq req) { + for (UserGroupMemberBatchReq.MemberWithAuthorities member : req.getMembers()) { + // 1. 检查用户是否已经是组织成员 + Optional existingMember = userGroupMembersRepository + .findByUserGrpOrgIdAndUserId(req.getOrgId(), member.getUserId()); + + if (!existingMember.isPresent()) { + // 如果不是成员,创建新的成员关系 + UserGroupMembers groupMember = new UserGroupMembers(); + groupMember.setUser_Grp_Org_ID(req.getOrgId()); + groupMember.setUser_ID(member.getUserId()); + groupMember.setR_Ship_Creator(req.getRelationshipType()); + groupMember.setCreation_Date_Time(new Date()); + + userGroupMembersRepository.save(groupMember); + } + + // 2. 删除现有权限(如果有) + String deleteSql = "DELETE FROM user_authority WHERE userId = :userId"; + Map params = new HashMap<>(); + params.put("userId", member.getUserId()); + jdbcDao.executeUpdate(deleteSql, params); + + // 3. 添加新的权限 + if (member.getAuthorityIds() != null && !member.getAuthorityIds().isEmpty()) { + for (Long authId : member.getAuthorityIds()) { + User_authority userAuthority = new User_authority(); + userAuthority.setUserId(member.getUserId()); + userAuthority.setAuthId(authId); + userAuthorityRepository.save(userAuthority); + } + } + } + } + + @Transactional + public void updateMember(Long userId, MemberUpdateReq req) { + Optional memberOpt = userGroupMembersRepository.findByUserGrpOrgIdAndUserId( + req.getOrgId(), userId); + + if (!memberOpt.isPresent()) { + throw new RuntimeException("Member not found"); + } + + UserGroupMembers member = memberOpt.get(); + member.setR_Ship_Creator(req.getRelationshipType()); + userGroupMembersRepository.save(member); + } + + @Transactional + public void removeMember(Long orgId, Long userId, String relationshipType) { + Optional memberOpt = userGroupMembersRepository.findByUserGrpOrgIdAndUserId(orgId, userId); + + if (!memberOpt.isPresent()) { + throw new RuntimeException("Member not found"); + } + + UserGroupMembers member = memberOpt.get(); + if (!member.getR_Ship_Creator().equals(relationshipType)) { + throw new RuntimeException("Invalid relationship type for member"); + } + + userGroupMembersRepository.deleteByUserGrpOrgIdAndUserId(orgId, userId); + } + + @Transactional(readOnly = true) + public List> getMemberAuthorities(int userId) { + String sql = "SELECT ua.*, a.name as auth_name " + + "FROM user_authority ua " + + "JOIN authority a ON ua.authId = a.id " + + "WHERE ua.userId = :userId"; + + Map params = new HashMap<>(); + params.put("userId", userId); + + return jdbcDao.queryForList(sql, params); + } + + @Transactional(readOnly = true) + public List> getAllAuthorities() { + return jdbcDao.queryForList("SELECT * FROM authority", new HashMap<>()); + } + + private void validatePermission(String relationshipType, String operation) { + if (!RelationshipRules.canPerformOperation(relationshipType, operation)) { + throw new RuntimeException("Operation not allowed for relationship type: " + relationshipType); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupMembersService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupMembersService.java new file mode 100644 index 0000000..31bbd7d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupMembersService.java @@ -0,0 +1,58 @@ +package com.ffii.fhsmsc.modules.User_group.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupMembers; +import com.ffii.fhsmsc.modules.User_group.repository.UserGroupMembersRepository; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupMembersReq; + +import jakarta.validation.Valid; + +@Service +public class UserGroupMembersService extends AbstractBaseEntityService { + public UserGroupMembersService(JdbcDao jdbcDao, UserGroupMembersRepository repository) { + super(jdbcDao, repository); + } + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM User_Group_Members fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("User_Grp_Org_ID")) sql.append(" AND fi.User_Grp_Org_ID = :User_Grp_Org_ID"); + if (args.containsKey("User_ID")) sql.append(" AND fi.User_ID = :User_ID"); + if (args.containsKey("R_ship_Creator")) sql.append(" AND fi.R_ship_Creator = :R_ship_Creator"); + } + + sql.append(" ORDER BY fi.User_Grp_Org_ID"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public UserGroupMembers saveOrUpdate(@Valid UserGroupMembersReq req) { + UserGroupMembers instance; + + if (req.getUser_Grp_Org_ID() != null) { + if(req.getUser_Grp_Org_ID()>0){ + instance = find(req.getUser_Grp_Org_ID()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new UserGroupMembers(); + } + } else { + instance = new UserGroupMembers(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupOrganizationService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupOrganizationService.java new file mode 100644 index 0000000..ccd29d2 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/UserGroupOrganizationService.java @@ -0,0 +1,58 @@ +package com.ffii.fhsmsc.modules.User_group.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupOrganization; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupOrganizationReq; +import com.ffii.fhsmsc.modules.User_group.repository.UserGroupOrganizationRepository; + +import jakarta.validation.Valid; + +@Service +public class UserGroupOrganizationService extends AbstractBaseEntityService { + public UserGroupOrganizationService(JdbcDao jdbcDao, UserGroupOrganizationRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM User_Group_Organization fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("User_Grp_Org_ID")) sql.append(" AND fi.User_Grp_Org_ID = :User_Grp_Org_ID"); + if (args.containsKey("User_Grp_Org_Name")) sql.append(" AND fi.User_Grp_Org_Name = :User_Grp_Org_Name"); + } + + sql.append(" ORDER BY fi.User_Grp_Org_ID"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public UserGroupOrganization saveOrUpdate(@Valid UserGroupOrganizationReq req) { + UserGroupOrganization instance; + + if (req.getUser_Grp_Org_ID() != null) { + if(req.getUser_Grp_Org_ID()>0){ + instance = find(req.getUser_Grp_Org_ID()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new UserGroupOrganization(); + } + } else { + instance = new UserGroupOrganization(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/User_authorityService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/User_authorityService.java new file mode 100644 index 0000000..f700105 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/User_authorityService.java @@ -0,0 +1,58 @@ +package com.ffii.fhsmsc.modules.User_group.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.User_authority; +import com.ffii.fhsmsc.modules.User_group.repository.User_authorityRepository; +import com.ffii.fhsmsc.modules.User_group.req.User_authorityReq; + +import jakarta.validation.Valid; + +@Service +public class User_authorityService extends AbstractBaseEntityService { + public User_authorityService(JdbcDao jdbcDao, User_authorityRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM user_authority fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("userId")) sql.append(" AND fi.userId = :userId"); + if (args.containsKey("authId")) sql.append(" AND fi.authId = :authId"); + } + + sql.append(" ORDER BY fi.userId"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public User_authority saveOrUpdate(@Valid User_authorityReq req) { + User_authority instance; + + if (req.getuserId() != null) { + if(req.getuserId()>0){ + instance = find(req.getuserId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new User_authority(); + } + } else { + instance = new User_authority(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/service/authorityService.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/authorityService.java new file mode 100644 index 0000000..19ed483 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/service/authorityService.java @@ -0,0 +1,58 @@ +package com.ffii.fhsmsc.modules.User_group.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.User_group.enity.authority; +import com.ffii.fhsmsc.modules.User_group.repository.authorityReponsitory; +import com.ffii.fhsmsc.modules.User_group.req.authorityReq; + +import jakarta.validation.Valid; + +@Service +public class authorityService extends AbstractBaseEntityService { + public authorityService(JdbcDao jdbcDao, authorityReponsitory repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM authority fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("id")) sql.append(" AND fi.id = :id"); + if (args.containsKey("authority")) sql.append(" AND fi.authority = :authority"); + } + + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public authority saveOrUpdate(@Valid authorityReq req) { + authority instance; + + if (req.getAuthority() != null) { + if(req.getAuthority().length()>0){ + instance = find(Long.parseLong(req.getAuthority())).orElseThrow(InternalServerErrorException::new); + } else { + instance = new authority(); + } + } else { + instance = new authority(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserCombineController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserCombineController.java new file mode 100644 index 0000000..14e7139 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserCombineController.java @@ -0,0 +1,85 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.fhsmsc.modules.User_group.req.DeleteMemberWithAuthoritiesReq; +import com.ffii.fhsmsc.modules.User_group.req.UserCombineReq; +import com.ffii.fhsmsc.modules.User_group.service.UserCombineService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/userCombine") +public class UserCombineController { + private final UserCombineService userCombineService; + + public UserCombineController(UserCombineService userCombineService) { + this.userCombineService = userCombineService; + } + + @Operation(summary = "Get user groups", security = @SecurityRequirement(name = "bearerAuth")) + @GetMapping("/list") + public RecordsRes> getUserGroups(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userCombineService.getUserGroups( + Long.parseLong(request.getParameter("userId")))); + } + + @Operation(summary = "Get group members", security = @SecurityRequirement(name = "bearerAuth")) + @GetMapping("/members/{orgId}") + public RecordsRes> getGroupMembers(@PathVariable Long orgId) { + return new RecordsRes<>(userCombineService.getGroupMembers(orgId)); + } + + @Operation(summary = "Get group detail", security = @SecurityRequirement(name = "bearerAuth")) + @GetMapping("/detail/{orgId}") + public Map getGroupDetail(@PathVariable Long orgId) { + return userCombineService.getGroupDetail(orgId); + } + + @Operation(summary = "Save user group", security = @SecurityRequirement(name = "bearerAuth")) + @PostMapping("/save") + public IdRes saveUserGroup(@RequestBody @Valid UserCombineReq req) { + Map result = userCombineService.saveUserGroup(req); + Number id = (Number) result.get("User_Grp_Org_ID"); + return new IdRes(id != null ? id.longValue() : null); + } + + @Operation(summary = "Delete user group", security = @SecurityRequirement(name = "bearerAuth")) + @DeleteMapping("/{orgId}") + public void deleteUserGroup(@PathVariable Long orgId, HttpServletRequest request) { + Long adminUserId = Long.parseLong(request.getParameter("adminUserId")); + userCombineService.deleteUserGroup(orgId, adminUserId); + } + + @Operation(summary = "Get user's organizations", security = @SecurityRequirement(name = "bearerAuth")) + @GetMapping("/user-groups/{userId}") + public RecordsRes> getUserOrganizations(@PathVariable Long userId) { + return new RecordsRes<>(userCombineService.getUserGroups(userId)); + } + + @Operation(summary = "Remove group member", security = @SecurityRequirement(name = "bearerAuth")) + @DeleteMapping("/members/{orgId}/{userId}") + public void removeGroupMember(@PathVariable Long orgId, @PathVariable Long userId) { + userCombineService.removeGroupMember(orgId, userId); + } + + @Operation(summary = "Delete member and related records", security = @SecurityRequirement(name = "bearerAuth")) + @DeleteMapping("/delete-member") + public void deleteMemberAndRelatedRecords(@RequestBody @Valid DeleteMemberWithAuthoritiesReq req) { + userCombineService.deleteMemberAndRelatedRecords(req); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserDelegationController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserDelegationController.java new file mode 100644 index 0000000..7504555 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserDelegationController.java @@ -0,0 +1,49 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.UserDelegationReq; +import com.ffii.fhsmsc.modules.User_group.service.UserDelegationService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/userDelegation") +public class UserDelegationController { + private UserDelegationService userDelegationService; + + public UserDelegationController( + UserDelegationService userDelegationService + ) { + this.userDelegationService = userDelegationService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userDelegationService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid UserDelegationReq req) { + return new IdRes(userDelegationService.saveOrUpdate (req).getId()); + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupAdminController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupAdminController.java new file mode 100644 index 0000000..9b6adb7 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupAdminController.java @@ -0,0 +1,49 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupAdminReq; +import com.ffii.fhsmsc.modules.User_group.service.UserGroupAdminService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/userGroupAdmin") +public class UserGroupAdminController { + private UserGroupAdminService userGroupAdminService; + + public UserGroupAdminController( + UserGroupAdminService userGroupAdminService + ) { + this.userGroupAdminService = userGroupAdminService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userGroupAdminService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid UserGroupAdminReq req) { + return new IdRes(userGroupAdminService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupCombineController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupCombineController.java new file mode 100644 index 0000000..bca14a3 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupCombineController.java @@ -0,0 +1,63 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import com.ffii.fhsmsc.modules.User_group.service.UserGroupCombineService; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupMemberBatchReq; +import com.ffii.fhsmsc.modules.User_group.req.MemberUpdateReq; +import com.ffii.fhsmsc.modules.User_group.enity.UserGroupMembers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import com.ffii.core.response.RecordsRes; +import com.ffii.fhsmsc.modules.User_group.req.*; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/usergroup") +public class UserGroupCombineController { + + @Autowired + private UserGroupCombineService userGroupCombineService; + + @GetMapping("/members/{orgId}") + public ResponseEntity> getGroupMembers( + @PathVariable Long orgId, + @RequestParam(required = false) String relationshipType) { + List members = userGroupCombineService.getGroupMembersWithAuthorities(orgId, relationshipType); + return ResponseEntity.ok(members); + } + + @PostMapping("/members/batch") + public ResponseEntity addMembers(@RequestBody UserGroupMemberBatchReq req) { + userGroupCombineService.addMemberWithAuthorities(req); + return ResponseEntity.ok().build(); + } + + @PutMapping("/members/{userId}") + public ResponseEntity updateMember( + @PathVariable Long userId, + @RequestBody MemberUpdateReq req) { + userGroupCombineService.updateMember(userId, req); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/members/{orgId}/{userId}") + public ResponseEntity removeMember( + @PathVariable Long orgId, + @PathVariable Long userId, + @RequestParam String relationshipType) { + userGroupCombineService.removeMember(orgId, userId, relationshipType); + return ResponseEntity.ok().build(); + } + + @GetMapping("/members/{userId}/authorities") + public RecordsRes> getMemberAuthorities(@PathVariable int userId) { + return new RecordsRes<>(userGroupCombineService.getMemberAuthorities(userId)); + } + + @GetMapping("/authorities") + public RecordsRes> getAllAuthorities() { + return new RecordsRes<>(userGroupCombineService.getAllAuthorities()); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupMembersController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupMembersController.java new file mode 100644 index 0000000..f1540da --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupMembersController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.User_group.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupMembersReq; +import com.ffii.fhsmsc.modules.User_group.service.UserGroupMembersService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/userGroupMembers") +public class UserGroupMembersController { + private UserGroupMembersService userGroupMembersService; + + public UserGroupMembersController( + UserGroupMembersService userGroupMembersService + ) { + this.userGroupMembersService = userGroupMembersService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userGroupMembersService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid UserGroupMembersReq req) { + return new IdRes(userGroupMembersService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupOrganizationController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupOrganizationController.java new file mode 100644 index 0000000..26556e1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/UserGroupOrganizationController.java @@ -0,0 +1,50 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.UserGroupOrganizationReq; +import com.ffii.fhsmsc.modules.User_group.service.UserGroupOrganizationService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/userGroupOrganization") +public class UserGroupOrganizationController { + private UserGroupOrganizationService userGroupOrganizationService; + + public UserGroupOrganizationController( + UserGroupOrganizationService userGroupOrganizationService + ) { + this.userGroupOrganizationService = userGroupOrganizationService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userGroupOrganizationService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid UserGroupOrganizationReq req) { + return new IdRes(userGroupOrganizationService.saveOrUpdate(req).getId()); + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/User_authorityController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/User_authorityController.java new file mode 100644 index 0000000..f7c490f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/User_authorityController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.User_authorityReq; +import com.ffii.fhsmsc.modules.User_group.service.User_authorityService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/user_authority") +public class User_authorityController { + private User_authorityService user_authorityService; + + public User_authorityController( + User_authorityService user_authorityService + ) { + this.user_authorityService = user_authorityService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(user_authorityService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid User_authorityReq req) { + return new IdRes(user_authorityService.saveOrUpdate (req).getId()); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/User_group/web/authorityController.java b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/authorityController.java new file mode 100644 index 0000000..a4c83de --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/User_group/web/authorityController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.User_group.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.User_group.req.authorityReq; +import com.ffii.fhsmsc.modules.User_group.service.authorityService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/authority") +public class authorityController { + private authorityService authorityService; + + public authorityController( + authorityService authorityService + ) { + this.authorityService = authorityService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(authorityService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid authorityReq req) { + return new IdRes(authorityService.saveOrUpdate (req).getId()); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/Allergy.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/Allergy.java new file mode 100644 index 0000000..d969659 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/Allergy.java @@ -0,0 +1,49 @@ +package com.ffii.fhsmsc.modules.allergy.enity; +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "allergies") +public class Allergy extends BaseEntity { + + @NotBlank + @Column + private int allergy_id; + + @Column + private String allergy_name; + + @Column + private LocalDateTime created_at; + + public int getAllergy_id() { + return allergy_id; + } + + public void setAllergy_id(int allergy_id) { + this.allergy_id = allergy_id; + } + + public String getallergy_name() { + return allergy_name; + } + + public void setallergy_name(String allergy_name) { + this.allergy_name = allergy_name; + } + + public LocalDateTime getcreated_at() { + return created_at; + } + + public void setcreated_at(LocalDateTime created_at) { + this.created_at = created_at; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/AllergyReponsitory.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/AllergyReponsitory.java new file mode 100644 index 0000000..130becb --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/AllergyReponsitory.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.allergy.enity; +import com.ffii.core.support.AbstractRepository; + +public interface AllergyReponsitory extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergy.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergy.java new file mode 100644 index 0000000..744da41 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergy.java @@ -0,0 +1,52 @@ +package com.ffii.fhsmsc.modules.allergy.enity; +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "user_allergies") +public class UserAllergy extends BaseEntity { + + @Column + private int user_allergy_id; + + @Column + private int user_id; + + @Column + private int allergy_id; + + @Column + private LocalDateTime created_at; + + public int getAllergy_id() { + return allergy_id; + } + + public void setAllergy_id(int allergy_id) { + this.allergy_id = allergy_id; + } + public int getUser_allergy_id() { + return user_allergy_id; + } + + public void setUser_allergy_id(int user_allergy_id) { + this.user_allergy_id = user_allergy_id; + } + + public int getUser_id() { + return user_id; + } + + public void setUser_id(int user_id) { + this.user_id = user_id; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergyReponsitory.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergyReponsitory.java new file mode 100644 index 0000000..430d7bc --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/enity/UserAllergyReponsitory.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.allergy.enity; +import com.ffii.core.support.AbstractRepository; + +public interface UserAllergyReponsitory extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/req/AllergyReq.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/req/AllergyReq.java new file mode 100644 index 0000000..469a47e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/req/AllergyReq.java @@ -0,0 +1,49 @@ +package com.ffii.fhsmsc.modules.allergy.req; +import java.time.LocalDateTime; +import java.util.List; +public class AllergyReq { + private Long allergy_id; + private Long user_id; + private String allergy_name; + private LocalDateTime created_at; + private List allergyIds; + + public List getAllergyIds() { + return allergyIds; + } + + public void setAllergyIds(List allergyIds) { + this.allergyIds = allergyIds; + } + + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + public Long getAllergy_id() { + return allergy_id; + } + + public void setAllergy_id(Long allergy_id) { + this.allergy_id = allergy_id; + } + + public String getAllergy_name() { + return allergy_name; + } + + public void setAllergy_name(String allergy_name) { + this.allergy_name = allergy_name; + } + + public LocalDateTime getCreated_at() { + return created_at; + } + + public void setCreated_at(LocalDateTime created_at) { + this.created_at = created_at; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/req/UserAllergyReq.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/req/UserAllergyReq.java new file mode 100644 index 0000000..f7e1ebe --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/req/UserAllergyReq.java @@ -0,0 +1,31 @@ +package com.ffii.fhsmsc.modules.allergy.req; +import java.time.LocalDateTime; +import java.util.List; + +public class UserAllergyReq { + private Long user_allergy_id; + private Long user_id; + private Long allergy_id; + public Long getUser_allergy_id() { + return user_allergy_id; + } + + public void setUser_allergy_id(Long user_allergy_id) { + this.user_allergy_id = user_allergy_id; + } + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + public Long getAllergy_id() { + return allergy_id; + } + + public void setAllergy_id(Long allergy_id) { + this.allergy_id = allergy_id; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/service/AllergyService.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/service/AllergyService.java new file mode 100644 index 0000000..5b933b5 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/service/AllergyService.java @@ -0,0 +1,93 @@ +package com.ffii.fhsmsc.modules.allergy.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.beans.factory.annotation.Autowired; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.allergy.enity.Allergy; +import com.ffii.fhsmsc.modules.allergy.enity.AllergyReponsitory; +import com.ffii.fhsmsc.modules.allergy.req.AllergyReq; +import java.util.ArrayList; +import java.util.HashMap; + +import jakarta.validation.Valid; +@Service +public class AllergyService extends AbstractBaseEntityService { + @Autowired + private JdbcTemplate jdbcTemplate; + + public AllergyService(JdbcDao jdbcDao, AllergyReponsitory repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder( + "SELECT a.allergy_id, " + + "i_en.value as name_en, " + + "i_zh.value as name_zh " + + "FROM `allergies` a " + + "LEFT JOIN i18n i_en ON a.allergy_id = i_en.record_id " + + "AND i_en.table_name = 'allergies' " + + "AND i_en.field_name = 'allergy_name' " + + "AND i_en.language = 'en' " + + "LEFT JOIN i18n i_zh ON a.allergy_id = i_zh.record_id " + + "AND i_zh.table_name = 'allergies' " + + "AND i_zh.field_name = 'allergy_name' " + + "AND i_zh.language = 'zh' " + + "WHERE 1=1"); + + if (args != null) { + if (args.containsKey("user_id")) { + sql.append(" AND a.user_id = :user_id"); + } + } + sql.append(" ORDER BY a.allergy_id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public Allergy saveOrUpdate(@Valid AllergyReq req) { + Allergy instance; + + if (req.getAllergy_id() != null) { + if(req.getAllergy_id()>0){ + instance = find(req.getAllergy_id()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new Allergy(); + } + } else { + instance = new Allergy(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public void updateUserAllergies(int userId, List allergyIds) { + + String deleteSql = "DELETE FROM user_allergies WHERE user_id = ?"; + jdbcTemplate.update(deleteSql, userId); + + + if (allergyIds != null && !allergyIds.isEmpty()) { + String insertSql = "INSERT INTO user_allergies (user_id, allergy_id, created_at) VALUES (?, ?, NOW())"; + List batchArgs = new ArrayList<>(); + + for (Integer allergyId : allergyIds) { + batchArgs.add(new Object[]{userId, allergyId}); + } + + jdbcTemplate.batchUpdate(insertSql, batchArgs); + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/service/UserAllergyService.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/service/UserAllergyService.java new file mode 100644 index 0000000..341c850 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/service/UserAllergyService.java @@ -0,0 +1,111 @@ +package com.ffii.fhsmsc.modules.allergy.service; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.Collections; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.beans.factory.annotation.Autowired; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.allergy.enity.UserAllergy; +import com.ffii.fhsmsc.modules.allergy.enity.UserAllergyReponsitory; +import com.ffii.fhsmsc.modules.allergy.req.UserAllergyReq; +import java.util.ArrayList; +import java.util.HashMap; + +import jakarta.validation.Valid; +@Service +public class UserAllergyService extends AbstractBaseEntityService { + @Autowired + private JdbcTemplate jdbcTemplate; + + public UserAllergyService(JdbcDao jdbcDao, UserAllergyReponsitory repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + // 检查是否提供了user_id + if (!args.containsKey("user_id")) { + throw new IllegalArgumentException("user_id is required"); + } + + StringBuilder sql = new StringBuilder( + "SELECT a.allergy_id, " + + "i_en.value as name_en, " + + "i_zh.value as name_zh " + + "FROM user_allergies ua " + + "JOIN allergies a ON ua.allergy_id = a.allergy_id " + + "LEFT JOIN i18n i_en ON a.allergy_id = i_en.record_id " + + "AND i_en.table_name = 'allergies' " + + "AND i_en.field_name = 'allergy_name' " + + "AND i_en.language = 'en' " + + "LEFT JOIN i18n i_zh ON a.allergy_id = i_zh.record_id " + + "AND i_zh.table_name = 'allergies' " + + "AND i_zh.field_name = 'allergy_name' " + + "AND i_zh.language = 'zh' " + + "WHERE ua.user_id = :user_id " + + "ORDER BY a.allergy_id"); + + List> results = jdbcDao.queryForList(sql.toString(), args); + + // 将结果转换为所需的格式 + List> allergyList = results.stream() + .map(row -> { + Map allergy = new HashMap<>(); + allergy.put("allergy_id", row.get("allergy_id")); + allergy.put("name_en", row.get("name_en")); + allergy.put("name_zh", row.get("name_zh")); + return allergy; + }) + .collect(Collectors.toList()); + + Map response = new HashMap<>(); + response.put("user_allerg", allergyList); + + return Collections.singletonList(response); + } + + @Transactional(rollbackFor = Exception.class) + public UserAllergy saveOrUpdate(@Valid UserAllergyReq req) { + UserAllergy instance; + + if (req.getAllergy_id() != null) { + if(req.getAllergy_id()>0){ + instance = find(req.getAllergy_id()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new UserAllergy(); + } + } else { + instance = new UserAllergy(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public void updateUserAllergies(int userId, List allergyIds) { + + String deleteSql = "DELETE FROM user_allergies WHERE user_id = ?"; + jdbcTemplate.update(deleteSql, userId); + + + if (allergyIds != null && !allergyIds.isEmpty()) { + String insertSql = "INSERT INTO user_allergies (user_id, allergy_id, created_at) VALUES (?, ?, NOW())"; + List batchArgs = new ArrayList<>(); + + for (Integer allergyId : allergyIds) { + batchArgs.add(new Object[]{userId, allergyId}); + } + + jdbcTemplate.batchUpdate(insertSql, batchArgs); + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/web/AllergyController.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/web/AllergyController.java new file mode 100644 index 0000000..5af5778 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/web/AllergyController.java @@ -0,0 +1,50 @@ +package com.ffii.fhsmsc.modules.allergy.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.allergy.req.AllergyReq; +import com.ffii.fhsmsc.modules.allergy.service.AllergyService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/allergy") +public class AllergyController { + private AllergyService allergyService; + public AllergyController( + AllergyService allergyService + ) { + this.allergyService = allergyService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(allergyService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid AllergyReq req) { + return new IdRes(allergyService.saveOrUpdate(req).getAllergy_id()); + } + @PostMapping("/update") + public void updateUserAllergies(@RequestBody @Valid AllergyReq req) { + allergyService.updateUserAllergies(req.getUser_id().intValue(), req.getAllergyIds()); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/allergy/web/UserAllergyController.java b/src/main/java/com/ffii/fhsmsc/modules/allergy/web/UserAllergyController.java new file mode 100644 index 0000000..6caa97c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/allergy/web/UserAllergyController.java @@ -0,0 +1,45 @@ +package com.ffii.fhsmsc.modules.allergy.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.allergy.req.UserAllergyReq; +import com.ffii.fhsmsc.modules.allergy.service.UserAllergyService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/user-allergy") +public class UserAllergyController { + private final UserAllergyService userAllergyService; + + public UserAllergyController(UserAllergyService userAllergyService) { + this.userAllergyService = userAllergyService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userAllergyService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid UserAllergyReq req) { + return new IdRes(userAllergyService.saveOrUpdate(req).getUser_allergy_id()); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/ErrorCodes.java b/src/main/java/com/ffii/fhsmsc/modules/common/ErrorCodes.java new file mode 100644 index 0000000..b21c67a --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/ErrorCodes.java @@ -0,0 +1,17 @@ +package com.ffii.fhsmsc.modules.common; + +public class ErrorCodes { + + public static final String FILE_UPLOAD_ERROR = "FILE_UPLOAD_ERROR"; + + public static final String STOCK_IN_WRONG_POST = "STOCK_IN_WRONG_POST"; + + public static final String USER_WRONG_NEW_PWD = "USER_WRONG_NEW_PWD"; + + public static final String SEND_EMAIL_ERROR = "SEND_EMAIL_ERROR"; + public static final String USERNAME_NOT_AVAILABLE = "USERNAME_NOT_AVAILABLE"; + + public static final String INIT_EXCEL_ERROR = "INIT_EXCEL_ERROR"; + + public static final String CHANGE_MAIN_CUSTOMER_ERROR = "CHANGE_MAIN_CUSTOMER_ERROR"; +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/LocalDateAdapter.java b/src/main/java/com/ffii/fhsmsc/modules/common/LocalDateAdapter.java new file mode 100644 index 0000000..43f379e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/LocalDateAdapter.java @@ -0,0 +1,23 @@ +package com.ffii.fhsmsc.modules.common; + +import com.google.gson.*; +import java.lang.reflect.Type; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public class LocalDateAdapter implements JsonSerializer, JsonDeserializer { + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Override + public JsonElement serialize(LocalDate date, Type type, JsonSerializationContext context) { + String dateString = date.format(DATE_FORMATTER); + return new JsonPrimitive(dateString); + } + + @Override + public LocalDate deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + String dateString = json.getAsString(); + return LocalDate.parse(dateString, DATE_FORMATTER); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/MailSMTP.java b/src/main/java/com/ffii/fhsmsc/modules/common/MailSMTP.java new file mode 100644 index 0000000..d4bfeef --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/MailSMTP.java @@ -0,0 +1,69 @@ +package com.ffii.fhsmsc.modules.common; + +import org.apache.commons.lang3.StringUtils; + +import com.ffii.fhsmsc.modules.settings.service.SettingsService; + +public class MailSMTP { + private String host; + private int port; + private String username; + private String password; + private boolean auth; + + public MailSMTP(SettingsService settingsService) { + if (settingsService == null) + throw new IllegalArgumentException("settingsService"); + + this.host = settingsService.getString(SettingNames.MAIL_SMTP_HOST); + this.port = settingsService.getInt(SettingNames.MAIL_SMTP_PORT); + this.username = settingsService.getString(SettingNames.MAIL_SMTP_USERNAME); + this.password = settingsService.getString(SettingNames.MAIL_SMTP_PASSWORD); + this.auth = settingsService.getBoolean(SettingNames.MAIL_SMTP_AUTH); + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean isAuth() { + return this.auth; + } + + public boolean getAuth() { + return this.auth; + } + + public void setAuth(boolean auth) { + this.auth = auth; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof MailSMTP)) + return false; + + MailSMTP o = (MailSMTP) obj; + if (StringUtils.equals(this.getHost(), o.getHost()) && + this.getPort() == o.getPort() && + StringUtils.equals(this.getUsername(), o.getUsername()) && + StringUtils.equals(this.getPassword(), o.getPassword())) { + return true; + } + + return false; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/PasswordRule.java b/src/main/java/com/ffii/fhsmsc/modules/common/PasswordRule.java new file mode 100644 index 0000000..bdf85a7 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/PasswordRule.java @@ -0,0 +1,119 @@ +package com.ffii.fhsmsc.modules.common; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ffii.core.utils.PasswordUtils.IPasswordRule; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; + + +public class PasswordRule implements IPasswordRule { + private Integer min; + + private Boolean numberAndAlphabetic; + private Boolean specialCharacter; + private Boolean notContainUsername; + private Boolean notContainThreeConsecutiveCharacters; + +public PasswordRule(SettingsService settingsService) { + if (settingsService == null){ + throw new IllegalArgumentException("settingsService"); + } + + this.min = settingsService.getInt(SettingNames.SYS_PASSWORD_RULE_MIN); + this.numberAndAlphabetic = settingsService.getBoolean(SettingNames.SYS_PASSWORD_RULE_NUMBER_AND_ALPHABETIC); + this.specialCharacter = settingsService.getBoolean(SettingNames.SYS_PASSWORD_RULE_SPECIAL_CHAR); + this.notContainUsername = settingsService.getBoolean(SettingNames.SYS_PASSWORD_RULE_NOT_CONTAIN_USERNAME); + this.notContainThreeConsecutiveCharacters = settingsService.getBoolean(SettingNames.SYS_PASSWORD_RULE_NOT_CONTAIN_THREE_CONSECUTIVE_CHAR); + } + + public Integer getMin() { + return this.min; + } + + public void setMin(Integer min) { + this.min = min; + } + + public Boolean isNumberAndAlphabetic() { + return this.numberAndAlphabetic; + } + + public Boolean getNumberAndAlphabetic() { + return this.numberAndAlphabetic; + } + + public void setNumberAndAlphabetic(Boolean numberAndAlphabetic) { + this.numberAndAlphabetic = numberAndAlphabetic; + } + + public Boolean isSpecialCharacter() { + return this.specialCharacter; + } + + public Boolean getSpecialCharacter() { + return this.specialCharacter; + } + + public void setSpecialCharacter(Boolean specialCharacter) { + this.specialCharacter = specialCharacter; + } + + public Boolean isNotContainUsername() { + return this.notContainUsername; + } + + public Boolean getNotContainUsername() { + return this.notContainUsername; + } + + public void setNotContainUsername(Boolean notContainUsername) { + this.notContainUsername = notContainUsername; + } + + public Boolean isNotContainThreeConsecutiveCharacters() { + return this.notContainThreeConsecutiveCharacters; + } + + public Boolean getNotContainThreeConsecutiveCharacters() { + return this.notContainThreeConsecutiveCharacters; + } + + public void setNotContainThreeConsecutiveCharacters(Boolean notContainThreeConsecutiveCharacters) { + this.notContainThreeConsecutiveCharacters = notContainThreeConsecutiveCharacters; + } + + @JsonIgnore + public String getWrongMsg() { + StringBuilder msg = new StringBuilder("Please Following Password Rule:\n"); + msg.append("- Minimum " + getMin() + " Characters.\n"); + if (needNumberAndAlphabetic()) + msg.append("- Need numbers and alphabetic characters.\n"); + if (needSpecialCharacter()) + msg.append("- Need at least one special characters.\n"); + if (needNotContainUsername()) + msg.append("- Cannot contain username.\n"); + if (needNotContainThreeConsecutiveCharacters()) + msg.append("- Cannot contain three consecutive characters.\n"); + return msg.toString(); + } + + @Override + public boolean needNumberAndAlphabetic() { + return numberAndAlphabetic; + } + + @Override + public boolean needSpecialCharacter() { + return specialCharacter; + } + + @Override + public boolean needNotContainUsername() { + return notContainUsername; + } + + @Override + public boolean needNotContainThreeConsecutiveCharacters() { + return notContainThreeConsecutiveCharacters; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/SecurityUtils.java b/src/main/java/com/ffii/fhsmsc/modules/common/SecurityUtils.java new file mode 100644 index 0000000..5ba8e13 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/SecurityUtils.java @@ -0,0 +1,146 @@ +package com.ffii.fhsmsc.modules.common; + +import java.util.Optional; + +import org.springframework.dao.DataAccessException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import com.ffii.fhsmsc.modules.user.entity.User; + +/** + * Security Utils - for Spring Security + * + * @author Patrick + */ +public class SecurityUtils { + + /** + * Obtains the current {@code SecurityContext}. + * + * @return the security context (never {@code null}) + */ + public static final SecurityContext getSecurityContext() { + return SecurityContextHolder.getContext(); + } + + /** + * @return the authenticated {@code Principal}) + * @see Authentication#getPrincipal() + */ + public static final Optional getUser() { + try { + return Optional.of((User) getSecurityContext().getAuthentication().getPrincipal()); + } catch (ClassCastException e) { + // no authenticated principal + return Optional.empty(); + } catch (NullPointerException e) { + // no authentication information is available + return Optional.empty(); + } + } + + /** + * Updates the Authentication Token with the user (e.g. user changed the password) + * + * @see SecurityContext#setAuthentication(Authentication) + */ + public static final void updateUserAuthentication(final UserDetails user) { + getSecurityContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities())); + } + + /** + * Checks if the current user is GRANTED the {@code role} + * + * @param role + * the {@code role} to check for + * @return {@code true} if the current user is GRANTED the {@code role}, else {@code false} + */ + public static final boolean isGranted(String role) { + Authentication authentication = getSecurityContext().getAuthentication(); + if (authentication == null) return false; + for (GrantedAuthority auth : authentication.getAuthorities()) { + if (role.equals(auth.getAuthority())) return true; + } + return false; + } + + /** + * Checks if the current user is NOT GRANTED the {@code role} + * + * @param role + * the {@code role} to check for + * @return {@code true} if the current user is NOT GRANTED the {@code role}, else {@code false} + */ + public static final boolean isNotGranted(String role) { + return !isGranted(role); + } + + /** + * Checks if the current user is GRANTED ANY of the {@code role}s + * + * @param roles + * the {@code role}s to check for + * @return {@code true} if the current user is GRANTED ANY of the {@code role}s, else {@code false} + */ + public static final boolean isGrantedAny(String... roles) { + for (int i = 0; i < roles.length; i++) { + if (isGranted(roles[i])) return true; + } + return false; + } + + /** + * Checks if the current user is NOT GRANTED ANY of the {@code role}s + * + * @param roles + * the {@code role}s to check for + * @return {@code true} if the current user is NOT GRANTED ANY of the {@code role}s, else {@code false} + */ + public static final boolean isNotGrantedAny(String... roles) { + return !isGrantedAny(roles); + } + + /** + * Checks if the current user is GRANTED ALL of the {@code role}s + * + * @param roles + * the {@code role}s to check for + * @return {@code true} if the current user is GRANTED ALL of the {@code role}s, else {@code false} + */ + public static final boolean isGrantedAll(String... roles) { + for (int i = 0; i < roles.length; i++) { + if (isNotGranted(roles[i])) return false; + } + return true; + } + + /** + * Login a user non-interactively + * + * @param userService + * any implementation of {@link UserDetailsService} + * @param username + * the username + * + * @throws UsernameNotFoundException + * if the user could not be found or the user has no GrantedAuthority + * @throws DataAccessException + * if user could not be found for a repository-specific reason + */ + public static final void loginUser(UserDetailsService userService, String username) { + /* load the user, throw exception if user not found */ + UserDetails userDetails = userService.loadUserByUsername(username); + + /* create authentication token for the specified user */ + Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); + getSecurityContext().setAuthentication(authentication); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/SettingNames.java b/src/main/java/com/ffii/fhsmsc/modules/common/SettingNames.java new file mode 100644 index 0000000..fade06b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/SettingNames.java @@ -0,0 +1,40 @@ +package com.ffii.fhsmsc.modules.common; + +public abstract class SettingNames { + /* + * System-wide settings + */ + + /** System Idle Logout Time*/ + public static final String SYS_IDLE_LOGOUT_TIME = "SYS.idleLogoutTime"; + + /* + * Mail settings + */ + + /** Mail - SMTP host */ + public static final String MAIL_SMTP_HOST = "MAIL.smtp.host"; + + /** Mail - SMTP port */ + public static final String MAIL_SMTP_PORT = "MAIL.smtp.port"; + + /** Mail - SMTP username */ + public static final String MAIL_SMTP_USERNAME = "MAIL.smtp.username"; + + /** Mail - SMTP password */ + public static final String MAIL_SMTP_PASSWORD = "MAIL.smtp.password"; + + /** Mail - SMTP auth */ + public static final String MAIL_SMTP_AUTH = "MAIL.smtp.auth"; + + public static final String SYS_PASSWORD_RULE_MIN = "SYS.password.rule.length.min"; + public static final String SYS_PASSWORD_RULE_DURATION = "SYS.password.rule.duration"; + public static final String SYS_PASSWORD_RULE_HISTORY = "SYS.password.rule.history"; + public static final String SYS_LOGIN_ATTEMPT_LIMIT = "SYS.loginAttempt.limit"; + public static final String SYS_LOGIN_ATTEMPT_PENALITY_TIME = "SYS.loginAttempt.penalityTime"; + + public static final String SYS_PASSWORD_RULE_NUMBER_AND_ALPHABETIC = "SYS.password.rule.numberAndAlphabetic"; + public static final String SYS_PASSWORD_RULE_SPECIAL_CHAR = "SYS.password.rule.specialCharacter"; + public static final String SYS_PASSWORD_RULE_NOT_CONTAIN_USERNAME = "SYS.password.rule.notContainUsername"; + public static final String SYS_PASSWORD_RULE_NOT_CONTAIN_THREE_CONSECUTIVE_CHAR = "SYS.password.rule.notContainThreeConsecutiveCharacters"; +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/mail/pojo/MailRequest.java b/src/main/java/com/ffii/fhsmsc/modules/common/mail/pojo/MailRequest.java new file mode 100644 index 0000000..290cac6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/mail/pojo/MailRequest.java @@ -0,0 +1,366 @@ +package com.ffii.fhsmsc.modules.common.mail.pojo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; + +public class MailRequest { + public final static int PRIORITY_HIGHEST = 1; + public final static int PRIORITY_HIGH = 2; + public final static int PRIORITY_NORMAL = 3; + public final static int PRIORITY_LOW = 4; + public final static int PRIORITY_LOWEST = 5; + + private InternetAddress from; + private List to; + + private String subject; + + private String template; + private String templateContent; + private Map args; + + private Integer priority; + + private InternetAddress replyTo; + private List cc; + private List bcc; + + private Map attachments; + + public MailRequest() { + } + + public static Builder builder() { + return new Builder(); + } + + public void addAttachment(String attachmentFilename, byte[] byteArray) { + if (this.attachments == null) this.attachments = new HashMap<>(); + this.attachments.put(attachmentFilename, byteArray); + } + + public void addTo(InternetAddress to) { + if (this.to == null) this.to = new ArrayList<>(); + this.to.add(to); + } + + public void addCc(InternetAddress cc) { + if (this.cc == null) this.cc = new ArrayList<>(); + this.cc.add(cc); + } + + public void addBcc(InternetAddress bcc) { + if (this.bcc == null) this.bcc = new ArrayList<>(); + this.bcc.add(bcc); + } + + // getter setter + + public InternetAddress getFrom() { + return from; + } + + public void setFrom(InternetAddress from) { + this.from = from; + } + + public List getTo() { + return to; + } + + public void setTo(List to) { + this.to = to; + } + + public void setTo(String[] to) throws AddressException { + if (to == null) { + this.to = null; + } else { + for (String a : to) { + this.addTo(new InternetAddress(a)); + } + } + } + + public void setStringListTo(List to) throws AddressException { + if (to == null) { + this.to = null; + } else { + for (String a : to) { + this.addTo(new InternetAddress(a)); + } + } + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + + public String getTemplateContent() { + return this.templateContent; + } + + public void setTemplateContent(String templateContent) { + this.templateContent = templateContent; + } + + public Map getArgs() { + return args; + } + + public void setArgs(Map args) { + this.args = args; + } + + public InternetAddress getReplyTo() { + return replyTo; + } + + public void setReplyTo(InternetAddress replyTo) { + this.replyTo = replyTo; + } + + public List getCc() { + return cc; + } + + public void setCc(List cc) { + this.cc = cc; + } + + public void setCc(String[] cc) throws AddressException { + if (cc == null) { + this.cc = null; + } else { + for (String a : cc) { + this.addCc(new InternetAddress(a)); + } + } + } + + public void setStringListCc(List cc) throws AddressException { + if (cc == null) { + this.cc = null; + } else { + for (String a : cc) { + this.addCc(new InternetAddress(a)); + } + } + } + + public List getBcc() { + return bcc; + } + + public void setBcc(List bcc) { + this.bcc = bcc; + } + + public void setBcc(String[] bcc) throws AddressException { + if (bcc == null) { + this.bcc = null; + } else { + for (String a : bcc) { + this.addBcc(new InternetAddress(a)); + } + } + } + + public void setStringListBcc(List bcc) throws AddressException { + if (bcc == null) { + this.bcc = null; + } else { + for (String a : bcc) { + this.addBcc(new InternetAddress(a)); + } + } + } + + public Map getAttachments() { + return attachments; + } + + public void setAttachments(Map attachments) { + this.attachments = attachments; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + // classes + + public static class Builder { + private MailRequest mailRequest; + + private Builder() { + this.mailRequest = new MailRequest(); + } + + public MailRequest build() { + return this.mailRequest; + } + + public Builder addAttachment(String attachmentFilename, byte[] byteArray) { + this.mailRequest.addAttachment(attachmentFilename, byteArray); + return this; + } + + public Builder addTo(InternetAddress to) { + this.mailRequest.addTo(to); + return this; + } + + public Builder addCc(InternetAddress cc) { + this.mailRequest.addCc(cc); + return this; + } + + public Builder addBcc(InternetAddress bcc) { + this.mailRequest.addBcc(bcc); + return this; + } + + public Builder from(InternetAddress from) { + this.mailRequest.setFrom(from); + return this; + } + + public Builder to(List to) { + this.mailRequest.setTo(to); + return this; + } + + public Builder to(String[] to) throws AddressException { + if (to == null) { + this.mailRequest.setTo((List) null); + } else { + for (String a : to) { + this.addTo(new InternetAddress(a)); + } + } + return this; + } + + public Builder stringListTo(List to) throws AddressException { + if (to == null) { + this.mailRequest.setTo((List) null); + } else { + for (String a : to) { + this.addTo(new InternetAddress(a)); + } + } + return this; + } + + public Builder subject(String subject) { + this.mailRequest.setSubject(subject); + return this; + } + + public Builder template(String template) { + this.mailRequest.setTemplate(template); + return this; + } + + public Builder templateContent(String templateContent) { + this.mailRequest.setTemplateContent(templateContent); + return this; + } + + public Builder args(Map args) { + this.mailRequest.setArgs(args); + return this; + } + + public Builder replyTo(InternetAddress replyTo) { + this.mailRequest.setReplyTo(replyTo); + return this; + } + + public Builder cc(List cc) { + this.mailRequest.setCc(cc); + return this; + } + + public Builder cc(String[] cc) throws AddressException { + if (cc == null) { + this.mailRequest.setCc((List) null); + } else { + for (String a : cc) { + this.addCc(new InternetAddress(a)); + } + } + return this; + } + + public Builder stringListCc(List cc) throws AddressException { + if (cc == null) { + this.mailRequest.setCc((List) null); + } else { + for (String a : cc) { + this.addCc(new InternetAddress(a)); + } + } + return this; + } + + public Builder bcc(List bcc) { + this.mailRequest.setBcc(bcc); + return this; + } + + public Builder bcc(String[] bcc) throws AddressException { + if (bcc == null) { + this.mailRequest.setBcc((List) null); + } else { + for (String a : bcc) { + this.addCc(new InternetAddress(a)); + } + } + return this; + } + + public Builder stringListBcc(List bcc) throws AddressException { + if (bcc == null) { + this.mailRequest.setBcc((List) null); + } else { + for (String a : bcc) { + this.addCc(new InternetAddress(a)); + } + } + return this; + } + + public Builder attachments(Map attachments) { + this.mailRequest.setAttachments(attachments); + return this; + } + + public Builder priority(Integer priority) { + this.mailRequest.setPriority(priority); + return this; + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailSenderService.java b/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailSenderService.java new file mode 100644 index 0000000..f8aa818 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailSenderService.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.common.mail.service; + +import java.util.Properties; + +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.stereotype.Service; + +import com.ffii.fhsmsc.modules.common.MailSMTP; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; + +/** caching mail sender if config no changed */ +@Service +public class MailSenderService { + + private SettingsService settingsService; + + private JavaMailSender sender; + private MailSMTP mailConfigCachs; + + public MailSenderService(SettingsService settingsService) { + this.settingsService = settingsService; + } + + public JavaMailSender get() { + MailSMTP config = new MailSMTP(settingsService); + if (this.sender == null || mailConfigCachs == null || !config.equals(this.mailConfigCachs)) { + this.mailConfigCachs = config; + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + + Properties props = new Properties(); + props.put("mail.smtp.timeout", "20000"); + props.put("mail.smtp.connectiontimeout", "10000"); + props.put("mail.smtp.auth", config.getAuth()); + props.put("mail.smtp.starttls.enable", "true"); + + sender.setHost(config.getHost()); + sender.setPort(config.getPort()); + sender.setUsername(config.getUsername()); + sender.setPassword(config.getPassword()); + sender.setJavaMailProperties(props); + + this.sender = sender; + } + + return this.sender; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailService.java b/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailService.java new file mode 100644 index 0000000..cda4060 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/mail/service/MailService.java @@ -0,0 +1,163 @@ +package com.ffii.fhsmsc.modules.common.mail.service; + +import java.io.IOException; +import java.text.ParseException; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.LocaleUtils; +import com.ffii.fhsmsc.modules.common.ErrorCodes; +import com.ffii.fhsmsc.modules.common.SettingNames; +import com.ffii.fhsmsc.modules.common.mail.pojo.MailRequest; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; + +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.MalformedTemplateNameException; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateNotFoundException; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; + +@Service +public class MailService { + protected final Log logger = LogFactory.getLog(getClass()); + + private JdbcDao jdbcDao; + private MailSenderService mailSenderService; + private Configuration freemarkerConfig; + private SettingsService settingsService; + + public MailService(JdbcDao jdbcDao, MailSenderService mailSenderService, Configuration freemarkerConfig, + SettingsService settingsService) { + this.jdbcDao = jdbcDao; + this.mailSenderService = mailSenderService; + this.freemarkerConfig = freemarkerConfig; + this.settingsService = settingsService; + } + + private void doSend(List mailRequests, Locale locale) + throws MessagingException, TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, + TemplateException { + JavaMailSender sender = mailSenderService.get(); + for (MailRequest mailRequest : mailRequests) { + MimeMessage mimeMessage = sender.createMimeMessage(); + + MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); + helper.setSubject(mailRequest.getSubject()); + + Template template = null; + if (mailRequest.getTemplate() != null) { + try { + template = freemarkerConfig + .getTemplate(mailRequest.getTemplate() + "_" + LocaleUtils.toLocaleStr(locale) + ".ftl"); + } catch (TemplateNotFoundException e) { + template = freemarkerConfig.getTemplate(mailRequest.getTemplate() + ".ftl"); + } + } else { + // Create a FreeMarker configuration + Configuration configuration = new Configuration(Configuration.VERSION_2_3_32); + + // Create a template loader and add the template content + StringTemplateLoader templateLoader = new StringTemplateLoader(); + templateLoader.putTemplate("myTemplate", mailRequest.getTemplateContent()); + configuration.setTemplateLoader(templateLoader); + + try { + // Get the template by name + template = configuration.getTemplate("myTemplate"); + } catch (IOException e) { + // Handle any IO exceptions + } + } + + helper.setText( + FreeMarkerTemplateUtils.processTemplateIntoString(template, mailRequest.getArgs()), + true); + if (mailRequest.getFrom() != null) { + helper.setFrom(mailRequest.getFrom()); + } else { + helper.setFrom(settingsService.getString(SettingNames.MAIL_SMTP_USERNAME)); + } + + if (mailRequest.getPriority() != null) + helper.setPriority(mailRequest.getPriority()); + if (mailRequest.getReplyTo() != null) + helper.setReplyTo(mailRequest.getReplyTo()); + if (mailRequest.getTo() != null) + helper.setTo(mailRequest.getTo().toArray(new InternetAddress[mailRequest.getTo().size()])); + if (mailRequest.getCc() != null) + helper.setCc(mailRequest.getCc().toArray(new InternetAddress[mailRequest.getCc().size()])); + if (mailRequest.getBcc() != null) + helper.setBcc(mailRequest.getBcc().toArray(new InternetAddress[mailRequest.getBcc().size()])); + + if (mailRequest.getAttachments() != null) { + for (Map.Entry entry : mailRequest.getAttachments().entrySet()) { + helper.addAttachment(entry.getKey(), new ByteArrayResource(entry.getValue())); + } + } + sender.send(mimeMessage); + } + } + + public void send(List mailRequests, Locale locale) throws ParseException { + try { + doSend(mailRequests, locale); + } catch (MessagingException | IOException | TemplateException e) { + throw new InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e); + } + } + + @Async + public void asyncSend(List mailRequests, Locale locale) throws ParseException { + try { + doSend(mailRequests, locale); + } catch (MessagingException | IOException | TemplateException e) { + logger.error("send email error", e); + } + } + + public void send(List mailRequests) throws ParseException { + send(mailRequests, LocaleUtils.getLocale()); + } + + @Async + public void asyncSend(List mailRequests) throws ParseException { + asyncSend(mailRequests, LocaleUtils.getLocale()); + } + + public void send(MailRequest mailRequest) throws ParseException { + send(Arrays.asList(mailRequest)); + } + + @Async + public void asyncSend(MailRequest mailRequest) throws ParseException { + asyncSend(Arrays.asList(mailRequest)); + } + + public void send(MailRequest mailRequest, Locale locale) throws ParseException { + send(Arrays.asList(mailRequest), locale); + } + + @Async + public void asyncSend(MailRequest mailRequest, Locale locale) throws ParseException { + asyncSend(Arrays.asList(mailRequest), locale); + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/common/service/AuditLogService.java b/src/main/java/com/ffii/fhsmsc/modules/common/service/AuditLogService.java new file mode 100644 index 0000000..826cce8 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/common/service/AuditLogService.java @@ -0,0 +1,107 @@ +package com.ffii.fhsmsc.modules.common.service; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.support.AbstractService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.MapUtils; + +import jakarta.annotation.Nullable; + +@Service +public class AuditLogService extends AbstractService { + + public AuditLogService(JdbcDao jdbcDao) { + super(jdbcDao); + } + + private static final String SQL_INSERT_AUDIT_LOG = "INSERT INTO audit_log (`tableName`, `recordId`, `recordName`, `modifiedBy`, `modified`, `oldData`, `newData`) " + + "VALUES (:tableName, :recordId, :recordName, :modifiedBy, :modified, :oldData, :newData)"; + + @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false) + public int save(String tableName, Long recordId, String recordName, Long modifiedBy, Date modified, @Nullable String oldData, String newData) { + return jdbcDao.executeUpdate(SQL_INSERT_AUDIT_LOG,MapUtils.toHashMap("tableName", tableName, "recordId", recordId, + "recordName",recordName, "modifiedBy", modifiedBy, "modified", modified, + "oldData", oldData, "newData", newData)); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public List> search(String tableName, Integer recordId) { + + String sql = "SELECT * FROM audit_log WHERE tableName = :tableName AND recordId = :recordId ORDER BY modified"; + + return jdbcDao.queryForList(sql, Map.of("tableName", tableName, "recordId", recordId)); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public List> search(String tableName, String recordId) { + + String sql = "SELECT * FROM audit_log WHERE tableName = :tableName AND recordId = :recordId ORDER BY modified"; + + return jdbcDao.queryForList(sql, Map.of("tableName", tableName, "recordId", recordId)); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public List> arsSearch(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " *, " + + " u.username " + + " FROM audit_log al" + + " LEFT JOIN user u on u.id = al.modifiedBy " + + " WHERE al.tableName is not NULL " + ); + + if (args != null) { + if (args.containsKey("tableName")) sql.append(" AND al.tableName = :tableName "); + if (args.containsKey("recordName")) sql.append(" AND al.recordName LIKE :recordName"); + if (args.containsKey("fromDate"))sql.append(" AND DATE(al.modified) >= :fromDate"); + if (args.containsKey("toDate"))sql.append(" AND DATE(al.modified) < :toDate"); + if (args.containsKey("modifiedBy")) sql.append(" AND al.modifiedBy = :modifiedBy"); + } + sql.append(" ORDER BY al.modified desc "); + return jdbcDao.queryForList(sql.toString(), args); + } + + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public List> getTables() { + String sql = "SELECT DISTINCT tableName FROM audit_log"; + + return jdbcDao.queryForList(sql, ""); + } + + public Map compareMaps(Map map1, Map map2) { + Map diffMap = new HashMap<>(); + + // Iterate over the entries of map2 + for (Map.Entry entry : map2.entrySet()) { + String key = entry.getKey(); + Object value2 = entry.getValue(); + Object value1 = map1.get(key); + + // Compare the values + if (value1 != null) { + if (value1.getClass().isArray() && value2.getClass().isArray()) { + if (!Arrays.deepEquals((Object[]) value1, (Object[]) value2)) { + diffMap.put(key, value2); + } + } else if (!value1.equals(value2)) { + diffMap.put(key, value2); + } + } + else if(value2 != null){ + diffMap.put(key, value2); + } + } + + return diffMap; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embedding.java b/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embedding.java new file mode 100644 index 0000000..7c9ebd1 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embedding.java @@ -0,0 +1,80 @@ +package com.ffii.fhsmsc.modules.embedding.enity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +/** @author Terence */ +@Entity +@Table(name = "menu_item_base_embedding") +public class embedding extends BaseEntity { + + @NotBlank + @Column + private Long embedding_id; + + @Column + private Long FB_menu_id; + + @Column + private String embedding_vector; + + @Column + private String embedding_text; + + @Column + private String language; + + public Long getembedding_id() { + return embedding_id; + } + + public void setembedding_id(Long embedding_id) { + this.embedding_id = embedding_id; + } + + public Long getFB_menu_id() { + return FB_menu_id; + } + + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + + public String getembedding_vector() { + return embedding_vector; + } + + public void setembedding_vector(String embedding_vector) { + this.embedding_vector = embedding_vector; + } + + public String getembedding_text() { + return embedding_text; + } + + public void setembedding_text(String embedding_text) { + this.embedding_text = embedding_text; + } + + public String getlanguage() { + return language; + } + + public void setlanguage(String language) { + this.language = language; + } + + + + + + + + + + + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embeddingRepository.java b/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embeddingRepository.java new file mode 100644 index 0000000..845846b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/embedding/enity/embeddingRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.embedding.enity; +import com.ffii.core.support.AbstractRepository; +public interface embeddingRepository extends AbstractRepository { + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/embedding/req/embeddingReq.java b/src/main/java/com/ffii/fhsmsc/modules/embedding/req/embeddingReq.java new file mode 100644 index 0000000..e6846e9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/embedding/req/embeddingReq.java @@ -0,0 +1,51 @@ +package com.ffii.fhsmsc.modules.embedding.req; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class embeddingReq { + @JsonProperty("embedding_id") + private Long embedding_id; + + @JsonProperty("FB_menu_id") + private Long FB_menu_id; + + @JsonProperty("embedding_vector") + private String embedding_vector; + + @JsonProperty("embedding_text") + private String embedding_text; + + @JsonProperty("language") + private String language; + + public Long getembedding_id() { + return embedding_id; + } + public void setembedding_id(Long embedding_id) { + this.embedding_id = embedding_id; + } + public Long getFB_menu_id() { + return FB_menu_id; + } + public void setFB_menu_id(Long FB_menu_id) { + this.FB_menu_id = FB_menu_id; + } + public String getembedding_vector() { + return embedding_vector; + } + public void setembedding_vector(String embedding_vector) { + this.embedding_vector = embedding_vector; + } + public String getembedding_text() { + return embedding_text; + } + public void setembedding_text(String embedding_text) { + this.embedding_text = embedding_text; + } + public String getlanguage() { + return language; + } + public void setlanguage(String language) { + this.language = language; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/embedding/service/embeddingService.java b/src/main/java/com/ffii/fhsmsc/modules/embedding/service/embeddingService.java new file mode 100644 index 0000000..a90445b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/embedding/service/embeddingService.java @@ -0,0 +1,87 @@ +package com.ffii.fhsmsc.modules.embedding.service; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.beans.BeanUtils; +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.embedding.enity.embedding; +import com.ffii.fhsmsc.modules.embedding.enity.embeddingRepository; +import com.ffii.fhsmsc.modules.embedding.req.embeddingReq; + +import jakarta.validation.Valid; + +@Service +public class embeddingService extends AbstractBaseEntityService { + + private static final Logger logger = LoggerFactory.getLogger(embeddingService.class); + + public embeddingService(JdbcDao jdbcDao, embeddingRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM menu_item_base_embedding fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("embedding_id")) sql.append(" AND fi.embedding_id = :embedding_id"); + if (args.containsKey("FB_menu_id")) sql.append(" AND fi.FB_menu_id = :FB_menu_id"); + if (args.containsKey("embedding_vector")) sql.append(" AND fi.embedding_vector = :embedding_vector"); + if (args.containsKey("embedding_text")) sql.append(" AND fi.embedding_text = :embedding_text"); + if (args.containsKey("language")) sql.append(" AND fi.language = :language"); + } + + sql.append(" ORDER BY fi.embedding_id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public embedding saveOrUpdate(@Valid embeddingReq req) { + logger.info("Request: embedding_id={}, FB_menu_id={}, embedding_vector={}, embedding_text={}, language={}", + req.getembedding_id(), req.getFB_menu_id(), req.getembedding_vector(), req.getembedding_text(), req.getlanguage()); + + embedding instance; + boolean isNew = false; + + if (req.getembedding_id() != null && req.getembedding_id() > 0) { + instance = find(req.getembedding_id()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new embedding(); + isNew = true; + } + + BeanUtils.copyProperties(req, instance); + logger.info("After BeanUtils: id={}, FB_menu_id={}, embedding_vector={}, embedding_text={}, language={}", + instance.getId(), instance.getFB_menu_id(), instance.getembedding_vector(), instance.getembedding_text(), instance.getlanguage()); + + if (instance.getCreated() == null) { + instance.setCreated(LocalDateTime.now()); + } + saveAndFlush(instance); + logger.info("After save: id={}, FB_menu_id={}, embedding_vector={}, embedding_text={}, language={}", + instance.getId(), instance.getFB_menu_id(), instance.getembedding_vector(), instance.getembedding_text(), instance.getlanguage()); + + return instance; + } + public List findAll() { + return repository.findAll(); + } + @Transactional(rollbackFor = Exception.class) + public void deleteByFBId(Long fbId) { + logger.info("Deleting operator with FB_id: {}", fbId); + String sql = "DELETE FROM fb_operator WHERE FB_id = :FB_id"; + Map args = new HashMap<>(); + args.put("FB_id", fbId); + jdbcDao.queryForList(sql, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/embedding/web/embeddingController.java b/src/main/java/com/ffii/fhsmsc/modules/embedding/web/embeddingController.java new file mode 100644 index 0000000..40ab1b6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/embedding/web/embeddingController.java @@ -0,0 +1,51 @@ +package com.ffii.fhsmsc.modules.embedding.web; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.embedding.req.embeddingReq; +import com.ffii.fhsmsc.modules.embedding.service.embeddingService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/embedding") +public class embeddingController { + private static final Logger logger = LoggerFactory.getLogger(embeddingController.class); + private embeddingService embeddingService; + + public embeddingController(embeddingService embeddingService) { + this.embeddingService = embeddingService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(embeddingService.search( + CriteriaArgsBuilder.withRequest(request) + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid embeddingReq req) { + try { + logger.info("Received save request: {}", req); + IdRes result = new IdRes(embeddingService.saveOrUpdate(req).getId()); + logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + logger.error("Error processing save request", e); + throw e; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/Req/fb_operator_and_organization_relationshipReq.java b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/Req/fb_operator_and_organization_relationshipReq.java new file mode 100644 index 0000000..6c303a6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/Req/fb_operator_and_organization_relationshipReq.java @@ -0,0 +1,30 @@ +package com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.Req; + +import com.fasterxml.jackson.annotation.JsonProperty; +public class fb_operator_and_organization_relationshipReq { + @JsonProperty("id") + private Long id; + @JsonProperty("FB_id") + private String FB_id; + @JsonProperty("User_Grp_Org_ID") + private String User_Grp_Org_ID; + public Long getId() { + return id; + } + public String getFB_id() { + return FB_id; + } + public String getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + public void setId(Long id) { + this.id = id; + } + public void setFB_id(String FB_id) { + this.FB_id = FB_id; + } + public void setUser_Grp_Org_ID(String User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationship.java b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationship.java new file mode 100644 index 0000000..3b48907 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationship.java @@ -0,0 +1,37 @@ +package com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.enity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "fb_operator_and_organization_relationship") +public class fb_operator_and_organization_relationship extends BaseEntity { + @Column + private Long id; + @Column + private String FB_id; + @Column + private String User_Grp_Org_ID; + + + + public String getFB_id() { + return FB_id; + } + + public String getUser_Grp_Org_ID() { + return User_Grp_Org_ID; + } + + public void setFB_id(String FB_id) { + this.FB_id = FB_id; + } + + public void setUser_Grp_Org_ID(String User_Grp_Org_ID) { + this.User_Grp_Org_ID = User_Grp_Org_ID; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationshipRepository.java b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationshipRepository.java new file mode 100644 index 0000000..c5539eb --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/enity/fb_operator_and_organization_relationshipRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.enity; + +import com.ffii.core.support.AbstractRepository; +public interface fb_operator_and_organization_relationshipRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/service/fb_operator_and_organization_relationshipService.java b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/service/fb_operator_and_organization_relationshipService.java new file mode 100644 index 0000000..dcc9523 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/service/fb_operator_and_organization_relationshipService.java @@ -0,0 +1,75 @@ +package com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.service; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.Req.fb_operator_and_organization_relationshipReq; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.enity.fb_operator_and_organization_relationship; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.enity.fb_operator_and_organization_relationshipRepository; + +import jakarta.validation.Valid; + +@Service +public class fb_operator_and_organization_relationshipService extends AbstractBaseEntityService { + private static final Logger logger = LoggerFactory.getLogger(fb_operator_and_organization_relationshipService.class); + + public fb_operator_and_organization_relationshipService(JdbcDao jdbcDao, fb_operator_and_organization_relationshipRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fo.*" + + " FROM `fb_operator_and_organization_relationship` fo" + + " WHERE 1=1"); + if (args.containsKey("FB_id")) { + sql.append(" AND fo.FB_id = :FB_id"); + } + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public fb_operator_and_organization_relationship saveOrUpdate(@Valid fb_operator_and_organization_relationshipReq req) { + logger.info("Request: id={}, FB_id={}, User_Grp_Org_ID={}", + req.getId(), req.getFB_id(), req.getUser_Grp_Org_ID()); + + fb_operator_and_organization_relationship instance; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new fb_operator_and_organization_relationship(); + } + + BeanUtils.copyProperties(req, instance); + logger.info("After BeanUtils: id={}, FB_id={}, User_Grp_Org_ID={}", + instance.getId(), instance.getFB_id(), instance.getUser_Grp_Org_ID()); + + if (instance.getCreated() == null) { + instance.setCreated(LocalDateTime.now()); + } + saveAndFlush(instance); + logger.info("After save: id={}, FB_id={}, User_Grp_Org_ID={}", + instance.getId(), instance.getFB_id(), instance.getUser_Grp_Org_ID()); + + return instance; + } + @Transactional(rollbackFor = Exception.class) + public void deleteByFBId(Long fbId) { + logger.info("Deleting organization relationships for FB_id: {}", fbId); + String sql = "DELETE FROM fb_operator_and_organization_relationship WHERE FB_id = :FB_id"; + Map args = new HashMap<>(); + args.put("FB_id", fbId); + jdbcDao.queryForList(sql, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/web/fb_operator_and_organization_relationshipController.java b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/web/fb_operator_and_organization_relationshipController.java new file mode 100644 index 0000000..3e4209c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/fb_operator_and_organization_relationship/web/fb_operator_and_organization_relationshipController.java @@ -0,0 +1,54 @@ +package com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.Req.fb_operator_and_organization_relationshipReq; +import com.ffii.fhsmsc.modules.fb_operator_and_organization_relationship.service.fb_operator_and_organization_relationshipService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/fb_operator_and_organization_relationship") +public class fb_operator_and_organization_relationshipController { + private fb_operator_and_organization_relationshipService fb_operator_and_organization_relationshipService; + + public fb_operator_and_organization_relationshipController( + fb_operator_and_organization_relationshipService fb_operator_and_organization_relationshipService + ) { + this.fb_operator_and_organization_relationshipService = fb_operator_and_organization_relationshipService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(fb_operator_and_organization_relationshipService.search( + CriteriaArgsBuilder.withRequest(request) + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid fb_operator_and_organization_relationshipReq req) { + try { + //logger.info("Received save request: {}", req); + IdRes result = new IdRes(fb_operator_and_organization_relationshipService.saveOrUpdate(req).getId()); + //logger.info("Successfully processed save request"); + return result; + } catch (Exception e) { + //logger.error("Error processing save request", e); + throw e; + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/File.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/File.java new file mode 100644 index 0000000..e08b380 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/File.java @@ -0,0 +1,85 @@ +package com.ffii.fhsmsc.modules.file.entity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Jason Lam */ +@Entity +@Table(name = "file") +public class File extends BaseEntity{ + + @NotBlank + @Column + private String filename; + + @NotBlank + @Column + private String skey; + + @Column + private String extension; + + @Column + private String mimetype; + + @Column + private Long filesize; + + @Column + private String remarks; + + public String getFilename() { + return this.filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getSkey() { + return this.skey; + } + + public void setSkey(String skey) { + this.skey = skey; + } + + public String getExtension() { + return this.extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getMimetype() { + return this.mimetype; + } + + public void setMimetype(String mimetype) { + this.mimetype = mimetype; + } + + public Long getFilesize() { + return this.filesize; + } + + public void setFilesize(Long filesize) { + this.filesize = filesize; + } + + public String getRemarks() { + return this.remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlob.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlob.java new file mode 100644 index 0000000..7a59c45 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlob.java @@ -0,0 +1,47 @@ +package com.ffii.fhsmsc.modules.file.entity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Jason Lam */ +@Entity +@Table(name = "file_blob") +public class FileBlob extends BaseEntity{ + @Column + private Long id; + + @Column + private Long fileId; + + @Column + private byte[] bytes; + + public Long getFileId() { + return this.fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public byte[] getBytes() { + return this.bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlobRepository.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlobRepository.java new file mode 100644 index 0000000..afcb4ea --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileBlobRepository.java @@ -0,0 +1,11 @@ +package com.ffii.fhsmsc.modules.file.entity; + +import java.util.Optional; +import org.springframework.data.repository.query.Param; + +import com.ffii.core.support.AbstractRepository; + +public interface FileBlobRepository extends AbstractRepository { + + Optional findByFileId(@Param("fileId") Long fileId); +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRef.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRef.java new file mode 100644 index 0000000..3592f1f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRef.java @@ -0,0 +1,86 @@ +package com.ffii.fhsmsc.modules.file.entity; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** @author Jason Lam */ +@Entity +@Table(name = "file_ref") +public class FileRef extends BaseEntity{ + + @Column + private Long fileId; + + @Column + private String refCode; + + @NotNull + @Column + private Long refId; + + @NotBlank + @Column + private String refType; + + @Column + private String remarks; + + @Column + private Long thumbnailFileId; + + + public Long getFileId() { + return this.fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public String getRefCode() { + return this.refCode; + } + + public void setRefCode(String refCode) { + this.refCode = refCode; + } + + public Long getRefId() { + return this.refId; + } + + public void setRefId(Long refId) { + this.refId = refId; + } + + public String getRefType() { + return this.refType; + } + + public void setRefType(String refType) { + this.refType = refType; + } + + public String getRemarks() { + return this.remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public Long getThumbnailFileId() { + return this.thumbnailFileId; + } + + public void setThumbnailFileId(Long thumbnailFileId) { + this.thumbnailFileId = thumbnailFileId; + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRefRepository.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRefRepository.java new file mode 100644 index 0000000..5cc14d0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRefRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.file.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface FileRefRepository extends AbstractRepository { +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRepository.java b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRepository.java new file mode 100644 index 0000000..7859df6 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/entity/FileRepository.java @@ -0,0 +1,5 @@ +package com.ffii.fhsmsc.modules.file.entity; +import com.ffii.core.support.AbstractRepository; + +public interface FileRepository extends AbstractRepository { +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/req/UpdateFileReq.java b/src/main/java/com/ffii/fhsmsc/modules/file/req/UpdateFileReq.java new file mode 100644 index 0000000..5c398d0 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/req/UpdateFileReq.java @@ -0,0 +1,83 @@ +package com.ffii.fhsmsc.modules.file.req; + +import jakarta.validation.constraints.Size; + +/** @author Jason Lam */ +public class UpdateFileReq { + + private Long id; + + @Size(max = 255) + private String fileName; + + @Size(max = 10) + private String extension; + + @Size(max = 10) + private String mimetype; + + private Integer filesize; + + @Size(max = 255) + private String remarks; + + private byte[] bytes; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFileName() { + return this.fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + + public String getExtension() { + return this.extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getMimetype() { + return this.mimetype; + } + + public void setMimetype(String mimetype) { + this.mimetype = mimetype; + } + + public Integer getFilesize() { + return this.filesize; + } + + public void setFilesize(Integer filesize) { + this.filesize = filesize; + } + + public String getRemarks() { + return this.remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public byte[] getBytes() { + return this.bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/service/FileBlobService.java b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileBlobService.java new file mode 100644 index 0000000..3d60088 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileBlobService.java @@ -0,0 +1,43 @@ +package com.ffii.fhsmsc.modules.file.service; + +import java.io.InputStream; +import java.util.Map; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.stereotype.Service; + +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.file.entity.FileBlob; +import com.ffii.fhsmsc.modules.file.entity.FileBlobRepository; + +@Service +public class FileBlobService extends AbstractBaseEntityService { + + public FileBlobService(JdbcDao jdbcDao, FileBlobRepository repository) { + super(jdbcDao, repository); + } + + public Long saveOrUpdate(Long fileId, InputStream inputStream) { + String sql = "INSERT INTO file_blob (fileId, bytes) VALUES (:fileId, :inputStream)"; + long rows = 0; + try { + rows = jdbcDao.executeUpdateAndReturnId(sql, Map.of("fileId", fileId, "inputStream", inputStream)); + inputStream.close(); + } catch (Exception e) { + logger.info(ExceptionUtils.getStackTrace(e)); + } + return rows; + } + + public Long saveByRepository(FileBlob obj) { + FileBlob temp = repository.save(obj); + return temp.getId(); + } + + public Long findIdByFileId(Long fileId) { + Map args = Map.of("fileId", fileId); + return (long) jdbcDao.queryForInt("SELECT fb.id " + + "FROM file_blob fb where fb.fileId = :fileId ", args); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/service/FileRefService.java b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileRefService.java new file mode 100644 index 0000000..c293348 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileRefService.java @@ -0,0 +1,91 @@ +package com.ffii.fhsmsc.modules.file.service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.file.entity.FileRef; +import com.ffii.fhsmsc.modules.file.entity.FileRefRepository; +@Service +public class FileRefService extends AbstractBaseEntityService { + + public FileRefService(JdbcDao jdbcDao, FileRefRepository repository) { + super(jdbcDao, repository); + } + + public Long saveOrUpdate(FileRef obj) { + FileRef instance; + if(obj.getId()!= null){ + instance = find(obj.getId()).get(); + } + else{ + instance = new FileRef(); + } + BeanUtils.copyProperties(obj,instance); + instance = save(instance); + + return instance.getId(); + } + + public List> listExternal(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fr.refType, " + + " fr.remarks, " + + " fr.id " + + " FROM file_ref fr " + + " LEFT JOIN file f ON f.id = fr.fileId " + + " WHERE fr.deleted = FALSE " + + " AND f.id IS NULL"); + + if (args != null) { + if (args.containsKey("refId")) + sql.append(" AND fr.refId = :refId "); + if (args.containsKey("refType")) + sql.append(" AND fr.refType = :refType"); + } + + sql.append(" ORDER BY fr.modified "); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public Optional findByFileId(Long fileId) { + Map args = Map.of("fileId", fileId); + return jdbcDao.queryForEntity("SELECT * "+ + "FROM file_ref fr where fr.fileId = :fileId " + , args, FileRef.class); + } + + public BigDecimal getFileStoredSize(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " COALESCE(SUM(f.filesize)/1024/1024,0) AS refSize " + + " FROM file_ref fr " + + " JOIN file f ON fr.fileId = f.id " + + " WHERE refId = :refId " + + " AND refType = :refType " + ); + + return jdbcDao.queryForDecimal(sql.toString(), args); + } + + public Integer getVideoExist(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " COALESCE(COUNT(f.id),0) AS videoCount " + + " FROM file_ref fr " + + " JOIN file f ON fr.fileId = f.id " + + " WHERE refId = :refId " + + " AND refType = :refType " + + " AND f.mimetype LIKE \"%video%\" " + ); + + return jdbcDao.queryForInt(sql.toString(), args); + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/service/FileService.java b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileService.java new file mode 100644 index 0000000..489dc88 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/service/FileService.java @@ -0,0 +1,313 @@ +package com.ffii.fhsmsc.modules.file.service; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Blob; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.imageio.ImageIO; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.bytedeco.javacv.FFmpegFrameGrabber; +import org.bytedeco.javacv.Frame; +import org.bytedeco.javacv.Java2DFrameConverter; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.FileUtils; +import com.ffii.core.utils.StringUtils; +import com.ffii.fhsmsc.modules.file.entity.File; +import com.ffii.fhsmsc.modules.file.entity.FileBlob; +import com.ffii.fhsmsc.modules.file.entity.FileRef; +import com.ffii.fhsmsc.modules.file.entity.FileRepository; + +@Service +public class FileService extends AbstractBaseEntityService { + + private FileBlobService fileBlobService; + private FileRefService fileRefService; + + private static final int NUM_THREADS = 4; // Number of threads to use for parallel processing + private ExecutorService executor; + + public FileService(JdbcDao jdbcDao, FileRepository repository, FileBlobService fileBlobService, + FileRefService fileRefService) { + super(jdbcDao, repository); + this.fileBlobService = fileBlobService; + this.fileRefService = fileRefService; + executor = Executors.newFixedThreadPool(NUM_THREADS); + } + + @Transactional + public Map deleteFile(Long id) { + File file = find(id).get(); + FileRef fileRef = fileRefService.findByFileId(id).get(); + Long fileBlobId = fileBlobService.findIdByFileId(id); + if (fileRef.getThumbnailFileId() != null) { + Long thumbnailId = fileRef.getThumbnailFileId(); + Long fileThumbnailBlobId = fileBlobService.find(thumbnailId).get().getId(); + fileBlobService.delete(fileThumbnailBlobId); + fileRefService.delete(thumbnailId); + } + delete(file.getId()); + fileRefService.delete(fileRef.getId()); + fileBlobService.delete(fileBlobId); + return Map.of( + "fileId", file.getId(), + "fileRefId", fileRef.getId(), + "fileBlobId", fileBlobId); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public Optional findFileByIdAndKey(Long fileId, String skey) { + + Map args = Map.of("fileId", fileId, "skey", skey); + return jdbcDao.queryForEntity("SELECT * " + + "FROM file f where f.id = :fileId " + + "AND f.skey = :skey", args, File.class); + } + + public Pair loadFile(Long id, String skey, String filename) { + // String data = id + skey + filename + FilenameUtils.getExtension(filename); + File file = findFileByIdAndKey(id, skey).get(); + byte[] fileByte = getfileBlob(id); + + return ImmutablePair.of(file, fileByte); + } + + public File loadFileOnly(Long id, String skey, String filename) { + // String data = id + skey + filename + FilenameUtils.getExtension(filename); + File file = findFileByIdAndKey(id, skey).get(); + return file; + } + + @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false) + public Long updateFileDisplay(Long fileId, MultipartFile file, String remarks) throws IOException { + // String data = id + skey + filename + FilenameUtils.getExtension(filename); + FileRef fileRef = fileRefService.findByFileId(fileId).get(); + fileRef.setRemarks(remarks); + + if (file != null) { + FileBlob thumbnail = fileBlobService.find(fileRef.getThumbnailFileId()).get(); + thumbnail.setBytes(file.getBytes()); + fileBlobService.save(thumbnail); + } + fileRefService.saveOrUpdate(fileRef); + + return fileRef.getId(); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public Optional findFileBlobByFileId(Long fileId) { + Map args = Map.of("fileId", fileId); + + return jdbcDao.queryForEntity("SELECT * " + + "FROM file_blob fb where fb.fileId = :fileId", args, FileBlob.class); + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public byte[] getfileBlob(Long fileId) { + Map args = Map.of("fileId", fileId); + + try { + Blob blob = jdbcDao + .queryForBlob("SELECT bytes FROM file_blob fb where fb.fileId = :fileId LIMIT 1", args); + int blobLength = (int) blob.length(); + byte[] bytes = blob.getBytes(1, blobLength); + blob.free(); + return bytes; + + } catch (Exception e) { + logger.info(ExceptionUtils.getStackTrace(e)); + } + + return null; + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true) + public byte[] getfileBlobById(Long id) { + Map args = Map.of("id", id); + + try { + Blob blob = jdbcDao + .queryForBlob("SELECT bytes FROM file_blob fb where fb.id = :id LIMIT 1", args); + int blobLength = (int) blob.length(); + byte[] bytes = blob.getBytes(1, blobLength); + blob.free(); + return bytes; + + } catch (Exception e) { + logger.info(ExceptionUtils.getStackTrace(e)); + } + + return null; + } + + @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = false) + public Map saveOrUpdate(MultipartFile[] files, Long refId, String refType) throws Exception { + List fileIdList = new ArrayList(); + List blobIdList = new ArrayList(); + List refIdList = new ArrayList(); + + for (MultipartFile multipartFile : files) { + File instance = new File(); + // byte[] compressedData = zipData(multipartFile.getBytes(), + // multipartFile.getOriginalFilename()); + + instance.setFilename(multipartFile.getOriginalFilename()); + instance.setSkey(RandomStringUtils.randomAlphanumeric(16)); + instance.setExtension(StringUtils.substringAfterLast(multipartFile.getOriginalFilename(), ".")); + instance.setMimetype(FileUtils.guessMimetype(instance.getFilename())); + instance.setFilesize(multipartFile.getSize()); + // instance.setFilesize(Long.parseLong(String.valueOf(compressedData.length))); + + instance = save(instance); + Long fileId = instance.getId(); + fileIdList.add(fileId); + // fileBlob.setBytes(compressedData); + Long blobId = fileBlobService.saveOrUpdate(fileId, multipartFile.getInputStream()); + blobIdList.add(blobId); + + FileRef fileRef = new FileRef(); + fileRef.setFileId(fileId); + fileRef.setRefId(refId); + fileRef.setRefType(refType); + + if (instance.getMimetype().equals("video/mpeg") || + instance.getMimetype().equals("video/mp4") || + instance.getMimetype().equals("video/x-matroska") || + instance.getMimetype().equals("video/quicktime") || + instance.getMimetype().equals("video/x-msvideo")) { + + byte[] thumbnailBlob; + if (instance.getMimetype().equals("video/mp4") || instance.getMimetype().equals("video/quicktime")) { + thumbnailBlob = processVideoAndGetThumbnail(multipartFile.getInputStream(), instance.getExtension(), + true); + + } else { + thumbnailBlob = processVideoAndGetThumbnail(multipartFile.getInputStream(), instance.getExtension(), + false); + } + Long thumbnailBlobId = fileBlobService.saveOrUpdate((long) -1, new ByteArrayInputStream(thumbnailBlob)); + fileRef.setThumbnailFileId(thumbnailBlobId); + } + + Long fileRefId = fileRefService.saveOrUpdate(fileRef); + refIdList.add(fileRefId); + } + + return Map.of( + "fileIdList", fileIdList, + "blobIdList", blobIdList, + "refIdList", refIdList); + } + + public List> listFile(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fr.refType, " + + " fr.remarks, " + + " fr.fileId, " + + " f.id, " + + " f.skey, " + + " f.extension, " + + " f.filename, " + + " f.filesize, " + + " f.mimetype " + + " FROM file_ref fr " + + " LEFT JOIN file f ON f.id = fr.fileId " + + " WHERE fr.deleted = FALSE " + + " AND f.id IS NOT NULL"); + + if (args != null) { + if (args.containsKey("refId")) + sql.append(" AND fr.refId = :refId "); + if (args.containsKey("refType")) + sql.append(" AND fr.refType = :refType"); + } + + sql.append(" ORDER BY fr.modified "); + + return jdbcDao.queryForList(sql.toString(), args); + } + + private byte[] processVideoAndGetThumbnail(InputStream inputStream, String extension, Boolean isMp4) + throws IOException { + + // TODO Temporary to avoid memroy leak using inpustream on FFmpegFrameGrabber #javacv 1.5.9 + FileOutputStream tempOutStream = null; + java.io.File tempFile = null; + byte[] thumbnailBlob = null; + try { + tempFile = java.io.File.createTempFile("temp", "." + extension); + tempOutStream = new FileOutputStream(tempFile); + IOUtils.copy(inputStream, tempOutStream); + + // Create a frame grabber to read the video blob + FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(tempFile); + if (isMp4) { + frameGrabber.setOption("-pix_fmt", "yuv420p"); // Set the pixel format option + } + frameGrabber.start(); + // 5000000 = 5 seconds + frameGrabber.setTimestamp(5000000); + + // Submit the frame grabbing task to the executor service + Future frameFuture = executor.submit(() -> frameGrabber.grabImage()); + + // Wait for the frame grabbing task to complete and get the frame + Frame frame; + frame = frameFuture.get(); + // Grab first frame if video < 5 seconds + if (frame == null) { + frameGrabber.setTimestamp(0); + frameFuture = executor.submit(() -> frameGrabber.grabImage()); + frame = frameFuture.get(); + } + + // Convert the frame to a BufferedImage + Java2DFrameConverter converter = new Java2DFrameConverter(); + BufferedImage thumbnailImage = converter.getBufferedImage(frame); + + // Convert the thumbnail image to a byte array + ByteArrayOutputStream thumbnailOutputStream = new ByteArrayOutputStream(); + ImageIO.write(thumbnailImage, "jpg", thumbnailOutputStream); + thumbnailBlob = thumbnailOutputStream.toByteArray(); + + // Clean up resources + frame.close(); + frameGrabber.close(); + thumbnailImage.flush(); + converter.close(); + thumbnailOutputStream.close(); + } catch (Exception e) { + // Handle any exceptions that occurred during frame grabbing + throw new IOException("Error occurred while grabbing frame", e); + } finally { + tempOutStream.close(); + inputStream.close(); + if (tempFile.exists()) { + tempFile.delete(); + } + } + + return thumbnailBlob; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/file/web/FileController.java b/src/main/java/com/ffii/fhsmsc/modules/file/web/FileController.java new file mode 100644 index 0000000..ffb1121 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/file/web/FileController.java @@ -0,0 +1,215 @@ +package com.ffii.fhsmsc.modules.file.web; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.imageio.ImageIO; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.http.CacheControl; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.utils.Params; +import com.ffii.fhsmsc.modules.file.entity.File; +import com.ffii.fhsmsc.modules.file.entity.FileRef; +import com.ffii.fhsmsc.modules.file.service.FileRefService; +import com.ffii.fhsmsc.modules.file.service.FileService; + +@RestController +@RequestMapping("/file") +public class FileController { + + private FileService fileService; + private FileRefService fileRefService; + + public FileController(FileService fileService, FileRefService fileRefService) { + this.fileService = fileService; + this.fileRefService = fileRefService; + } + + @DeleteMapping("/{id}") + public Map delete(@PathVariable Long id) { + return fileService.deleteFile(id); + } + + @PostMapping(value = "/thumbnail/ul", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public Map saveOrUpdate( + @RequestParam Long fileId, + @RequestParam(value = "remarks", required = false) String remarks, + @RequestParam(value = "file", required = false) MultipartFile file) + throws Exception { + + return Map.of(Params.DATA, fileService.updateFileDisplay(fileId, file, remarks)); + } + + @RequestMapping(value = "/dl/{id}/{skey}/{filename}", method = RequestMethod.GET) + public ResponseEntity download(@RequestParam(defaultValue = "false") boolean dl, + @PathVariable Long id, @PathVariable String skey) throws Exception { + + File file = fileService.findFileByIdAndKey(id, skey) + .orElseThrow(NotFoundException::new); + + return ResponseEntity.ok() + .cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS)) + .header("filename", file.getFilename()) + .body(new ByteArrayResource(fileService.getfileBlob(file.getId()))); + //.body(new ByteArrayResource(unzippedData)); + } + + @RequestMapping(value ="/thumbnail/{id}/{skey}/{filename}", method = RequestMethod.GET) + public ResponseEntity thumbnail( + @RequestParam(defaultValue = "false") boolean dl, + @PathVariable Long id, + @PathVariable String skey, + @PathVariable String filename) throws IOException { + + File quickRef = fileService.loadFileOnly(id,skey,filename); + + if(quickRef.getMimetype().equals("application/x-7z-compressed") || + quickRef.getMimetype().equals("application/x-rar-compressed") || + quickRef.getMimetype().equals("application/zip") + ){ + return null; + } + + if(quickRef.getMimetype().equals("video/mpeg") || + quickRef.getMimetype().equals("video/mp4") || + quickRef.getMimetype().equals("video/x-matroska") || + quickRef.getMimetype().equals("video/quicktime") || + quickRef.getMimetype().equals("video/x-msvideo") + ){ + FileRef fileRef = fileRefService.findByFileId(quickRef.getId()).get(); + byte[] thumbnailBlob = fileService.getfileBlobById(fileRef.getThumbnailFileId()); + //byte[] thumbnailBlob = processVideoAndGetThumbnail(unzippedData); + + // Create an appropriate HTTP response with the thumbnail blob + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_JPEG); + + return ResponseEntity.ok() + .header("filename", quickRef.getFilename()) + .body(new ByteArrayResource(thumbnailBlob)); + } + + List typeToGenerateThumbnail = List.of( + "image/jpeg", + "image/png", + "application/pdf" + ); + + if(typeToGenerateThumbnail.contains(quickRef.getMimetype())){ + Pair pair = fileService.loadFile(id, skey, filename); + if (pair.getLeft() != null && pair.getRight() != null) { + File file = pair.getLeft(); + byte[] fileByte = pair.getRight(); + + if (file.getMimetype().equals("image/jpeg") + || file.getMimetype().equals("image/png") + ) { + int limit = 500; + BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileByte)); + //BufferedImage image = ImageIO.read(new ByteArrayInputStream(unzippedData)); + int width = image.getWidth(); + int height = image.getHeight(); + if (width > height) { + if (width > limit) { + height = height * limit / width; + width = limit; + } + } else { + if (height > limit) { + width = width * limit / height; + height = limit; + } + } + image = scale(image, width, height); + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + ImageIO.write(image, "jpg", tmp); + tmp.close(); + + return ResponseEntity.ok() + .cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS)) + .header("filename", file.getFilename()) + .body(new ByteArrayResource(tmp.toByteArray())); + } + + if(file.getMimetype().equals("application/pdf") + ){ + BufferedImage thumbnailPdf = generateThumbnailFromPdf(fileByte); + //BufferedImage thumbnailPdf = generateThumbnailFromPdf(unzippedData); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(thumbnailPdf, "jpg", baos); + byte[] thumbnailBytes = baos.toByteArray(); + // Create an appropriate HTTP response with the thumbnail blob + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_JPEG); + + return ResponseEntity.ok() + .cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS)) + .header("filename", file.getFilename()) + .body(new ByteArrayResource(thumbnailBytes)); + } + } + } + else{ + return null; + } + + return null; + } + + private BufferedImage generateThumbnailFromPdf(byte[] pdfBytes) throws IOException { + // Load the PDF document from the byte array + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + // Create a PDF renderer + PDFRenderer pdfRenderer = new PDFRenderer(document); + + // Render the first page of the PDF to an image + BufferedImage pageImage = pdfRenderer.renderImageWithDPI(0, 300); // Adjust DPI as needed + + // Generate a thumbnail image by scaling down the page image + int thumbnailWidth = 200; // Adjust the thumbnail width as needed + int thumbnailHeight = (int) ((double) thumbnailWidth / pageImage.getWidth() * pageImage.getHeight()); + BufferedImage thumbnailImage = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB); + thumbnailImage.createGraphics().drawImage(pageImage.getScaledInstance(thumbnailWidth, thumbnailHeight, BufferedImage.SCALE_SMOOTH), 0, 0, null); + + return thumbnailImage; + } + } + + + static BufferedImage scale(BufferedImage originalImage, int w, int h) { + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + int x, y; + int ww = originalImage.getWidth(); + int hh = originalImage.getHeight(); + for (x = 0; x < w; x++) { + for (y = 0; y < h; y++) { + int col = originalImage.getRGB(x * ww / w, y * hh / h); + img.setRGB(x, y, col); + } + } + return img; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1.java b/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1.java new file mode 100644 index 0000000..e73f21e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1.java @@ -0,0 +1,290 @@ +package com.ffii.fhsmsc.modules.food1.entity; +import java.math.BigDecimal; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +/** @author Terence */ +@Entity +@Table(name = "food1") +public class Food1 extends BaseEntity{ + @Column(name = "name") + private String name; + + @Column(name = "Weight") + private BigDecimal weight; + + @Column(name = "Calories") + private BigDecimal calories; + + @Column(name = "Protein") + private BigDecimal protein; + + @Column(name = "Fat") + private BigDecimal fat; + + @Column(name = "Carbohydrate") + private BigDecimal carbohydrate; + + @Column(name = "Sodium") + private BigDecimal sodium; + + @Column(name = "Potassium") + private BigDecimal potassium; + + @Column(name = "Calcium") + private BigDecimal calcium; + + @Column(name = "Phosphorus") + private BigDecimal phosphorus; + + @Column(name = "Iron") + private BigDecimal iron; + + @Column(name = "VitaminA") + private BigDecimal vitaminA; + + @Column(name = "VitaminB1") + private BigDecimal vitaminB1; + + @Column(name = "VitaminB2") + private BigDecimal vitaminB2; + + @Column(name = "Niacin") + private BigDecimal niacin; + + @Column(name = "VitaminC") + private BigDecimal vitaminC; + + @Column(name = "food_type") + private String foodType; + + @Column(name = "contains_egg") + private Boolean containsEgg; + + @Column(name = "contains_milk") + private Boolean containsMilk; + + @Column(name = "contains_wheat") + private Boolean containsWheat; + + @Column(name = "contains_shrimp") + private Boolean containsShrimp; + + @Column(name = "contains_crab") + private Boolean containsCrab; + + @Column(name = "contains_buckwheat") + private Boolean containsBuckwheat; + + @Column(name = "contains_peanut") + private Boolean containsPeanut; + + @Column(name = "contains_walnut") + private Boolean containsWalnut; + + @Column(name = "Restaurant_ID") + private Integer restaurant_id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getWeight() { + return weight; + } + + public void setWeight(BigDecimal weight) { + this.weight = weight; + } + + public BigDecimal getCalories() { + return calories; + } + + public void setCalories(BigDecimal calories) { + this.calories = calories; + } + + public BigDecimal getProtein() { + return protein; + } + + public void setProtein(BigDecimal protein) { + this.protein = protein; + } + + public BigDecimal getFat() { + return fat; + } + + public void setfat(BigDecimal fat) { + this.fat = fat; + } + + public BigDecimal getCarbohydrate() { + return carbohydrate; + } + + public void setCarbohydrate(BigDecimal carbohydrate) { + this.carbohydrate = carbohydrate; + } + + public BigDecimal getSodium() { + return sodium; + } + + public void setSodium(BigDecimal sodium) { + this.sodium = sodium; + } + + public String getFoodType() { + return foodType; + } + + public void setFoodType(String foodType) { + this.foodType = foodType; + } + + public Boolean getContainsEgg() { + return containsEgg; + } + + public void setContainsEgg(Boolean containsEgg) { + this.containsEgg = containsEgg; + } + + public Boolean getContainsMilk() { + return containsMilk; + } + + public void setContainsMilk(Boolean containsMilk) { + this.containsMilk = containsMilk; + } + + public Boolean getContainsWheat() { + return containsWheat; + } + + public void setContainsWheat(Boolean containsWheat) { + this.containsWheat = containsWheat; + } + + public Boolean getContainsShrimp() { + return containsShrimp; + } + + public void setContainsShrimp(Boolean containsShrimp) { + this.containsShrimp = containsShrimp; + } + + public Boolean getContainsCrab() { + return containsCrab; + } + + public void setContainsCrab(Boolean containsCrab) { + this.containsCrab = containsCrab; + } + + public Boolean getContainsBuckwheat() { + return containsBuckwheat; + } + + public void setContainsBuckwheat(Boolean containsBuckwheat) { + this.containsBuckwheat = containsBuckwheat; + } + + public Boolean getContainsPeanut() { + return containsPeanut; + } + + public void setContainsPeanut(Boolean containsPeanut) { + this.containsPeanut = containsPeanut; + } + + public Boolean getContainsWalnut() { + return containsWalnut; + } + + public void setContainsWalnut(Boolean containsWalnut) { + this.containsWalnut = containsWalnut; + } + public BigDecimal getPotassium() { + return potassium; + } + + public void setPotassium(BigDecimal potassium) { + this.potassium = potassium; + } + public BigDecimal getCalcium() { + return calcium; + } + + public void setCalcium(BigDecimal calcium) { + this.calcium = calcium; + } + public BigDecimal getPhosphorus() { + return phosphorus; + } + + public void setPhosphorus(BigDecimal phosphorus) { + this.phosphorus = phosphorus; + } + public BigDecimal getIron() { + return iron; + } + + public void setIron(BigDecimal iron) { + this.iron = iron; + } + public BigDecimal getVitaminA() { + return vitaminA; + } + + public void setVitaminA(BigDecimal vitaminA) { + this.vitaminA = vitaminA; + } + public BigDecimal getVitaminB1() { + return vitaminB1; + } + + public void setVitaminB1(BigDecimal vitaminB1) { + this.vitaminB1 = vitaminB1; + } + public BigDecimal getVitaminB2() { + return vitaminB2; + } + + public void setVitaminB2(BigDecimal vitaminB2) { + this.vitaminB2 = vitaminB2; + } + public BigDecimal getNiacin() { + return niacin; + } + + public void setNiacin(BigDecimal niacin) { + this.niacin = niacin; + } + public BigDecimal getVitaminC() { + return vitaminC; + } + + public void setVitaminC(BigDecimal vitaminC) { + this.vitaminC = vitaminC; + } + + public Integer getRestaurant_id() { + return restaurant_id; + } + + public void setRestaurant_id(Integer restaurant_id) { + this.restaurant_id = restaurant_id; + + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1Repository.java b/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1Repository.java new file mode 100644 index 0000000..6c009bd --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/food1/entity/Food1Repository.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.food1.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface Food1Repository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/food1/req/SaveFood1Req.java b/src/main/java/com/ffii/fhsmsc/modules/food1/req/SaveFood1Req.java new file mode 100644 index 0000000..a582785 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/food1/req/SaveFood1Req.java @@ -0,0 +1,257 @@ +package com.ffii.fhsmsc.modules.food1.req; +import java.math.BigDecimal; + +import jakarta.validation.constraints.NotNull; + +public class SaveFood1Req { + private Long id; + @NotNull + private String name; + private BigDecimal weight; + private BigDecimal calories; + private BigDecimal protein; + private BigDecimal fat; + private BigDecimal carbohydrate; + private BigDecimal sodium; + private BigDecimal potassium; + private BigDecimal calcium; + private BigDecimal phosphorus; + private BigDecimal iron; + private BigDecimal vitaminA; + private BigDecimal vitaminB1; + private BigDecimal vitaminB2; + private BigDecimal niacin; + private BigDecimal vitaminC; + private String foodType; + private Integer contains_egg; + private Integer contains_milk; + private Integer contains_wheat; + private Integer contains_shrimp; + private Integer contains_crab; + private Integer contains_buckwheat; + private Integer contains_peanut; + private Integer contains_walnut; + private Integer restaurant_id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + public String getfood_type() { + return foodType; + } + + public void setfood_type(String foodType) { + this.foodType = foodType; + } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getWeight() { + return weight; + } + + public void setWeight(BigDecimal weight) { + this.weight = weight; + } + + public BigDecimal getCalories() { + return calories; + } + + public void setCalories(BigDecimal calories) { + this.calories = calories; + } + + public BigDecimal getProtein() { + return protein; + } + + public void setProtein(BigDecimal protein) { + this.protein = protein; + } + + public BigDecimal getFat() { + return fat; + } + + public void setfat(BigDecimal fat) { + this.fat = fat; + } + + public BigDecimal getCarbohydrate() { + return carbohydrate; + } + + public void setCarbohydrate(BigDecimal carbohydrate) { + this.carbohydrate = carbohydrate; + } + + public BigDecimal getSodium() { + return sodium; + } + + public void setSodium(BigDecimal sodium) { + this.sodium = sodium; + } + + public BigDecimal getPotassium() { + return potassium; + } + + public void setPotassium(BigDecimal potassium) { + this.potassium = potassium; + } + + public BigDecimal getCalcium() { + return calcium; + } + + public void setCalcium(BigDecimal calcium) { + this.calcium = calcium; + } + + public BigDecimal getPhosphorus() { + return phosphorus; + } + + public void setPhosphorus(BigDecimal phosphorus) { + this.phosphorus = phosphorus; + } + + public BigDecimal getIron() { + return iron; + } + + public void setIron(BigDecimal iron) { + this.iron = iron; + } + + public BigDecimal getVitaminA() { + return vitaminA; + } + + public void setVitaminA(BigDecimal vitaminA) { + this.vitaminA = vitaminA; + } + + public BigDecimal getVitaminB1() { + return vitaminB1; + } + + public void setVitaminB1(BigDecimal vitaminB1) { + this.vitaminB1 = vitaminB1; + } + + public BigDecimal getVitaminB2() { + return vitaminB2; + } + + public void setVitaminB2(BigDecimal vitaminB2) { + this.vitaminB2 = vitaminB2; + } + + public BigDecimal getNiacin() { + return niacin; + } + + public void setNiacin(BigDecimal niacin) { + this.niacin = niacin; + } + + public BigDecimal getVitaminC() { + return vitaminC; + } + + public void setVitaminC(BigDecimal vitaminC) { + this.vitaminC = vitaminC; + } + + public String getFoodType() { + return foodType; + } + + public void setFoodType(String foodType) { + this.foodType = foodType; + } + + public Integer getContainsEgg() { + return contains_egg; + } + + public void setContainsEgg(Integer contains_egg) { + this.contains_egg = contains_egg; + } + + public Integer getContainsMilk() { + return contains_milk; + } + + public void setContainsMilk(Integer contains_milk) { + this.contains_milk = contains_milk; + } + + public Integer getContainsWheat() { + return contains_wheat; + } + + public void setContainsWheat(Integer contains_wheat) { + this.contains_wheat = contains_wheat; + } + + public Integer getContainsShrimp() { + return contains_shrimp; + } + + public void setContainsShrimp(Integer contains_shrimp) { + this.contains_shrimp = contains_shrimp; + } + + public Integer getContainsCrab() { + return contains_crab; + } + + public void setContainsCrab(Integer contains_crab) { + this.contains_crab = contains_crab; + } + + public Integer getContainsBuckwheat() { + return contains_buckwheat; + } + + public void setContainsBuckwheat(Integer contains_buckwheat) { + this.contains_buckwheat = contains_buckwheat; + } + + public Integer getContainsPeanut() { + return contains_peanut; + } + + public void setContainsPeanut(Integer contains_peanut) { + this.contains_peanut = contains_peanut; + } + + public Integer getContainsWalnut() { + return contains_walnut; + } + + public void setContainsWalnut(Integer contains_walnut) { + this.contains_walnut = contains_walnut; + } + + public Integer getRestaurant_id() { + return restaurant_id; + } + + public void setRestaurant_id(Integer restaurant_id) { + this.restaurant_id = restaurant_id; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/food1/service/Food1Service.java b/src/main/java/com/ffii/fhsmsc/modules/food1/service/Food1Service.java new file mode 100644 index 0000000..c6b4606 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/food1/service/Food1Service.java @@ -0,0 +1,80 @@ +package com.ffii.fhsmsc.modules.food1.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.food1.entity.Food1; +import com.ffii.fhsmsc.modules.food1.entity.Food1Repository; +import com.ffii.fhsmsc.modules.food1.req.SaveFood1Req; + +import jakarta.validation.Valid; + +@Service +public class Food1Service extends AbstractBaseEntityService{ + public Food1Service(JdbcDao jdbcDao, Food1Repository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.id," + + " fi.name," + + " fi.`Weight`," + + " fi.`Calories`," + + " fi.`Protein`," + + " fi.`Fat`," + + " fi.`Carbohydrate`," + + " fi.`Sodium`," + + " fi.`Potassium`," + + " fi.`Calcium`, fi.`Phosphorus`, fi.`Iron`," + + " fi.`VitaminA`, fi.`VitaminB1`, fi.`VitaminB2`," + + " fi.`Niacin`, fi.`VitaminC`, " + + " fi.food_type," + + " fi.contains_egg," + + " fi.contains_milk," + + " fi.contains_wheat," + + " fi.contains_shrimp," + + " fi.contains_crab," + + " fi.contains_buckwheat," + + " fi.contains_peanut," + + " fi.contains_walnut," + + " fi.restaurant_id" + + " FROM food1 fi" + + " WHERE 1=1"); + + if (args != null) { + if (args.containsKey("id")) sql.append(" AND fi.id = :id"); + if (args.containsKey("name")) sql.append(" AND fi.name LIKE :name"); + if (args.containsKey("food_type")) sql.append(" AND fi.food_type = :food_type"); + } + + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public Food1 saveOrUpdate(@Valid SaveFood1Req req) { + Food1 instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new Food1(); + } + } else { + instance = new Food1(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/food1/web/Food1Controller.java b/src/main/java/com/ffii/fhsmsc/modules/food1/web/Food1Controller.java new file mode 100644 index 0000000..39a20bc --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/food1/web/Food1Controller.java @@ -0,0 +1,46 @@ +package com.ffii.fhsmsc.modules.food1.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.food1.req.SaveFood1Req; +import com.ffii.fhsmsc.modules.food1.service.Food1Service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/food1") +public class Food1Controller { + private Food1Service food1Service; + + public Food1Controller( + Food1Service food1Service + ) { + this.food1Service = food1Service; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(food1Service.search( + CriteriaArgsBuilder.withRequest(request) + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid SaveFood1Req req) { + return new IdRes(food1Service.saveOrUpdate(req).getId()); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItems.java b/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItems.java new file mode 100644 index 0000000..1a5f979 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItems.java @@ -0,0 +1,232 @@ +package com.ffii.fhsmsc.modules.foodItems.entity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "food_items") +public class FoodItems extends BaseEntity { + + @NotBlank + @Column + private String name; + + @Column + private BigDecimal kcal; + + @Column + private BigDecimal carbo; + + @Column + private LocalDateTime date; + + @Column + private BigDecimal fat; + + @Column + private String meal; + + @Column + private BigDecimal protein; + + + @Column + private BigDecimal size; + + + @Column + private BigDecimal sodium; + + @Column + private BigDecimal potassium; + + @Column + private BigDecimal calcium; + + @Column + private BigDecimal phosphorus; + + @Column + private BigDecimal iron; + + @Column + private BigDecimal vitaminA; + + @Column + private BigDecimal vitaminB1; + + @Column + private BigDecimal vitaminB2; + + @Column + private BigDecimal niacin; + + @Column + private BigDecimal vitaminC; + + @Column + private Long user_id; + + + + // getter & setter + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getKcal() { + return kcal; + } + + public void setKcal(BigDecimal kcal) { + this.kcal = kcal; + } + + public BigDecimal getCarbo() { + return carbo; + } + + public void setCarbo(BigDecimal carbo) { + this.carbo = carbo; + } + + public LocalDateTime getDate() { + return date; + } + + public void setDate(LocalDateTime date) { + this.date = date; + } + + public BigDecimal getFat() { + return fat; + } + + public void setFat(BigDecimal fat) { + this.fat = fat; + } + + public String getMeal() { + return meal; + } + + public void setMeal(String meal) { + this.meal = meal; + } + + public BigDecimal getProtein() { + return protein; + } + + public void setProtein(BigDecimal protein) { + this.protein = protein; + } + + public BigDecimal getSize() { + return size; + } + + public void setSize(BigDecimal size) { + this.size = size; + } + + public BigDecimal getSodium() { + return sodium; + } + + public void setSodium(BigDecimal sodium) { + this.sodium = sodium; + } + public BigDecimal getpotassium() { + return potassium; + } + + public void setpotassium(BigDecimal potassium) { + this.potassium = potassium; + } + + public BigDecimal getcalcium() { + return calcium; + } + + public void setcalcium(BigDecimal calcium) { + this.calcium = calcium; + } + + public BigDecimal getphosphorus() { + return phosphorus; + } + + public void setphosphorus(BigDecimal phosphorus) { + this.phosphorus = phosphorus; + } + public BigDecimal getiron() { + return iron; + } + + public void setiron(BigDecimal iron) { + this.iron = iron; + } + + public BigDecimal getvitaminA() { + return vitaminA; + } + + public void setvitaminA(BigDecimal vitaminA) { + this.vitaminA = vitaminA; + } + + public BigDecimal getvitaminB1() { + return vitaminB1; + } + + public void setvitaminB1(BigDecimal vitaminB1) { + this.vitaminB1 = vitaminB1; + } + + public BigDecimal getvitaminB2() { + return vitaminB2; + } + + public void setvitaminB2(BigDecimal vitaminB2) { + this.vitaminB2 = vitaminB2; + } + + public BigDecimal getniacin() { + return niacin; + } + + public void setniacin(BigDecimal niacin) { + this.niacin = niacin; + } + public BigDecimal getvitaminC() { + return vitaminC; + } + + public void setvitaminC(BigDecimal vitaminC) { + this.vitaminC = vitaminC; + } + + + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItemsRepository.java b/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItemsRepository.java new file mode 100644 index 0000000..b911c5d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/foodItems/entity/FoodItemsRepository.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.foodItems.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface FoodItemsRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/foodItems/req/SaveFoodItemsReq.java b/src/main/java/com/ffii/fhsmsc/modules/foodItems/req/SaveFoodItemsReq.java new file mode 100644 index 0000000..5864667 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/foodItems/req/SaveFoodItemsReq.java @@ -0,0 +1,216 @@ +package com.ffii.fhsmsc.modules.foodItems.req; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import jakarta.validation.constraints.NotNull; + +public class SaveFoodItemsReq { + private Long id; + + @NotNull + private String name; + private BigDecimal kcal; + private BigDecimal carbo; + private LocalDateTime date; + private BigDecimal fat; + private String meal; + private BigDecimal protein; + private BigDecimal size; + private BigDecimal sodium; + private BigDecimal potassium; + + private BigDecimal calcium; + + private BigDecimal phosphorus; + + private BigDecimal iron; + + private BigDecimal vitaminA; + + private BigDecimal vitaminB1; + + private BigDecimal vitaminB2; + + private BigDecimal niacin; + + private BigDecimal vitaminC; + private Long user_id; + + //private LocalDateTime created; + + private String meal_type; + /* + public LocalDateTime getCreated() { + return created; + } + + public void setCreated(LocalDateTime created) { + this.created = created; + } + */ + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getKcal() { + return kcal; + } + + public void setKcal(BigDecimal kcal) { + this.kcal = kcal; + } + + public BigDecimal getCarbo() { + return carbo; + } + + public void setCarbo(BigDecimal carbo) { + this.carbo = carbo; + } + + public LocalDateTime getDate() { + return date; + } + + public void setDate(LocalDateTime date) { + this.date = date; + } + + public BigDecimal getFat() { + return fat; + } + + public void setFat(BigDecimal fat) { + this.fat = fat; + } + + public String getMeal() { + return meal; + } + + public void setMeal(String meal) { + this.meal = meal; + } + + public BigDecimal getProtein() { + return protein; + } + + public void setProtein(BigDecimal protein) { + this.protein = protein; + } + + public BigDecimal getSize() { + return size; + } + + public void setSize(BigDecimal size) { + this.size = size; + } + + public BigDecimal getSodium() { + return sodium; + } + + public void setSodium(BigDecimal sodium) { + this.sodium = sodium; + } + + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + + public BigDecimal getPotassium() { + return potassium; + } + + public void setPotassium(BigDecimal potassium) { + this.potassium = potassium; + } + + public BigDecimal getCalcium() { + return calcium; + } + + public void setCalcium(BigDecimal calcium) { + this.calcium = calcium; + } + + public BigDecimal getPhosphorus() { + return phosphorus; + } + + public BigDecimal getIron() { + return iron; + } + + public void setIron(BigDecimal iron) { + this.iron = iron; + } + + public BigDecimal getVitaminA() { + return vitaminA; + } + + public void setVitaminA(BigDecimal vitaminA) { + this.vitaminA = vitaminA; + } + + public BigDecimal getVitaminB1() { + return vitaminB1; + } + + public void setVitaminB1(BigDecimal vitaminB1) { + this.vitaminB1 = vitaminB1; + } + + public BigDecimal getVitaminB2() { + return vitaminB2; + } + + public void setVitaminB2(BigDecimal vitaminB2) { + this.vitaminB2 = vitaminB2; + } + + public BigDecimal getNiacin() { + return niacin; + } + + public void setNiacin(BigDecimal niacin) { + this.niacin = niacin; + } + + public BigDecimal getVitaminC() { + return vitaminC; + } + + public void setVitaminC(BigDecimal vitaminC) { + this.vitaminC = vitaminC; + } + + public String getMeal_type() { + return meal_type; + } + + public void setMeal_type(String meal_type) { + this.meal_type = meal_type; + } +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/foodItems/service/FoodItemsService.java b/src/main/java/com/ffii/fhsmsc/modules/foodItems/service/FoodItemsService.java new file mode 100644 index 0000000..5bda46c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/foodItems/service/FoodItemsService.java @@ -0,0 +1,443 @@ +package com.ffii.fhsmsc.modules.foodItems.service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.enity.DailyNutritionSummary; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.req.DailyNutritionSummaryReq; +import com.ffii.fhsmsc.modules.DailyNutritionSummary.service.DailyNutritionSummaryService; +import com.ffii.fhsmsc.modules.foodItems.entity.FoodItems; +import com.ffii.fhsmsc.modules.foodItems.entity.FoodItemsRepository; +import com.ffii.fhsmsc.modules.foodItems.req.SaveFoodItemsReq; + +import jakarta.validation.Valid; + +@Service +public class FoodItemsService extends AbstractBaseEntityService{ + private final DailyNutritionSummaryService dailyNutritionSummaryService; + + public FoodItemsService(JdbcDao jdbcDao, FoodItemsRepository repository, + DailyNutritionSummaryService dailyNutritionSummaryService) { + super(jdbcDao, repository); + this.dailyNutritionSummaryService = dailyNutritionSummaryService; + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `food_items` fi" + + " WHERE fi.deleted = FALSE"); + + if (args != null) { + if (args.containsKey("userId")) { + sql.append(" AND fi.user_id = :userId"); + } + if (args.containsKey("date")) { + sql.append(" AND DATE(fi.date) = DATE(:date)"); + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + /** + * 根据日期范围和用户ID查询食物项 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @param userId 用户ID + * @return 符合条件的食物项列表 + */ + public List> searchByDateRange(LocalDate startDate, LocalDate endDate, Integer userId) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `food_items` fi" + + " WHERE fi.deleted = FALSE"); + + // 添加日期范围条件 + sql.append(" AND DATE(fi.date) BETWEEN :startDate AND :endDate"); + + // 添加用户ID条件 + sql.append(" AND fi.user_id = :userId"); + + // 按日期和ID排序 + sql.append(" ORDER BY fi.date, fi.id"); + + // 准备参数 + Map params = new HashMap<>(); + params.put("startDate", java.sql.Date.valueOf(startDate)); + params.put("endDate", java.sql.Date.valueOf(endDate)); + params.put("userId", userId); + + // 执行查询 + return jdbcDao.queryForList(sql.toString(), params); + } + +@Transactional(rollbackFor = Exception.class) + public FoodItems saveOrUpdate(@Valid SaveFoodItemsReq req) { + FoodItems instance; + boolean isNew = false; + + if (req.getId() != null && req.getId() > 0) { + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new FoodItems(); + isNew = true; + } + + BeanUtils.copyProperties(req, instance); + if (instance.getCreated() == null) { + instance.setCreated(LocalDateTime.now()); + } + + // 先保存食物项 + saveAndFlush(instance); + + // 更新每日营养汇总数据 + updateDailyNutritionSummary(instance, isNew); + + return instance; + } + + /** + * 更新每日营养汇总数据 + */ + private void updateDailyNutritionSummary(FoodItems foodItem, boolean isNew) { + if (foodItem.getUser_id() == null || foodItem.getDate() == null) { + return; // 如果没有用户ID或日期,则不更新汇总数据 + } + + // 获取食物项的日期 + LocalDate localDate = foodItem.getDate().toLocalDate(); + Date foodDate = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + + // 查找当天的营养汇总记录 + Map args = new HashMap<>(); + args.put("user_id", foodItem.getUser_id()); + args.put("date", foodDate); // 添加日期参数,确保只查询当天的记录 + List> summaries = dailyNutritionSummaryService.search(args); + + DailyNutritionSummaryReq summaryReq = new DailyNutritionSummaryReq(); + boolean found = false; + + // 查找对应日期的汇总记录(由于已经按日期过滤,这里只需要检查是否有记录) + if (!summaries.isEmpty()) { + Map item = summaries.get(0); + // 安全地将 id 转换为 Long 类型 + Object idObj = item.get("id"); + if (idObj instanceof Integer) { + summaryReq.setId(((Integer) idObj).longValue()); + } else if (idObj instanceof Long) { + summaryReq.setId((Long) idObj); + } else if (idObj != null) { + summaryReq.setId(Long.valueOf(idObj.toString())); + } + found = true; + } + + if (found) { + // 如果找到了记录,获取当前值并更新 + DailyNutritionSummary summary = dailyNutritionSummaryService.find(summaryReq.getId()).orElse(null); + if (summary != null) { + // 复制现有记录的值到请求对象 + copyNutritionValues(summaryReq, summary); + + // 更新营养汇总数据 + if (isNew) { + // 如果是新增食物项,增加营养值 + addNutritionValues(summaryReq, foodItem); + } else { + // 如果是更新食物项,重新计算当天所有食物项的总和 + recalculateNutritionSummary(summaryReq, foodItem.getUser_id(), localDate); + } + } + } else { + // 如果没有找到记录,创建新记录 + createNewNutritionSummary(summaryReq, foodItem, foodDate); + } + + // 确保 user_id 字段不为 null + if (summaryReq.getUser_id() == null) { + summaryReq.setUser_id(foodItem.getUser_id()); + } + + // 保存更新后的营养汇总数据 + dailyNutritionSummaryService.saveOrUpdate(summaryReq); + } + + /** + * 创建新的营养汇总记录 + */ + private void createNewNutritionSummary(DailyNutritionSummaryReq summaryReq, FoodItems foodItem, Date foodDate) { + summaryReq.setUser_id(foodItem.getUser_id()); + summaryReq.setDate(foodDate); + + // 设置创建和更新时间 + LocalDateTime now = LocalDateTime.now(); + summaryReq.setCreated(now); + summaryReq.setModified(now); + summaryReq.setDeleted(false); + summaryReq.setVersion(0); + + // 初始化营养值为0 + initializeNutritionValues(summaryReq); + + // 添加食物项的营养值 + addNutritionValues(summaryReq, foodItem); + } + + /** + * 复制营养汇总记录的值 + */ + private void copyNutritionValues(DailyNutritionSummaryReq summaryReq, DailyNutritionSummary summary) { + summaryReq.setTotal_calories(summary.getTotal_calories() != null ? summary.getTotal_calories() : BigDecimal.ZERO); + summaryReq.setTotal_protein(summary.getTotal_protein() != null ? summary.getTotal_protein() : BigDecimal.ZERO); + summaryReq.setTotal_fat(summary.getTotal_fat() != null ? summary.getTotal_fat() : BigDecimal.ZERO); + summaryReq.setTotal_carbs(summary.getTotal_carbs() != null ? summary.getTotal_carbs() : BigDecimal.ZERO); + summaryReq.setTotal_sodium(summary.getTotal_sodium() != null ? summary.getTotal_sodium() : BigDecimal.ZERO); + summaryReq.setTotal_potassium(summary.getTotal_potassium() != null ? summary.getTotal_potassium() : BigDecimal.ZERO); + summaryReq.setTotal_calcium(summary.getTotal_calcium() != null ? summary.getTotal_calcium() : BigDecimal.ZERO); + summaryReq.setTotal_phosphorus(summary.getTotal_phosphorus() != null ? summary.getTotal_phosphorus() : BigDecimal.ZERO); + summaryReq.setTotal_iron(summary.getTotal_iron() != null ? summary.getTotal_iron() : BigDecimal.ZERO); + summaryReq.setTotal_vitamin_a(summary.getTotal_vitamin_a() != null ? summary.getTotal_vitamin_a() : BigDecimal.ZERO); + summaryReq.setTotal_vitamin_b1(summary.getTotal_vitamin_b1() != null ? summary.getTotal_vitamin_b1() : BigDecimal.ZERO); + summaryReq.setTotal_vitamin_b2(summary.getTotal_vitamin_b2() != null ? summary.getTotal_vitamin_b2() : BigDecimal.ZERO); + summaryReq.setTotal_niacin(summary.getTotal_niacin() != null ? summary.getTotal_niacin() : BigDecimal.ZERO); + summaryReq.setTotal_vitamin_c(summary.getTotal_vitamin_c() != null ? summary.getTotal_vitamin_c() : BigDecimal.ZERO); + summaryReq.setBreakfast_calories(summary.getBreakfast_calories() != null ? summary.getBreakfast_calories() : BigDecimal.ZERO); + summaryReq.setLunch_calories(summary.getLunch_calories() != null ? summary.getLunch_calories() : BigDecimal.ZERO); + summaryReq.setDinner_calories(summary.getDinner_calories() != null ? summary.getDinner_calories() : BigDecimal.ZERO); + summaryReq.setSnack_calories(summary.getSnack_calories() != null ? summary.getSnack_calories() : BigDecimal.ZERO); + + // 保留其他元数据 + summaryReq.setUser_id(summary.getUser_id()); + summaryReq.setDate(summary.getDate()); + summaryReq.setCreated(summary.getCreated()); + summaryReq.setCreatedBy(summary.getCreatedBy()); + summaryReq.setModified(LocalDateTime.now()); + summaryReq.setDeleted(summary.getDeleted()); + summaryReq.setVersion(summary.getVersion()); + } + + /** + * 初始化营养值为0 + */ + private void initializeNutritionValues(DailyNutritionSummaryReq summaryReq) { + summaryReq.setTotal_calories(BigDecimal.ZERO); + summaryReq.setTotal_protein(BigDecimal.ZERO); + summaryReq.setTotal_fat(BigDecimal.ZERO); + summaryReq.setTotal_carbs(BigDecimal.ZERO); + summaryReq.setTotal_sodium(BigDecimal.ZERO); + summaryReq.setTotal_potassium(BigDecimal.ZERO); + summaryReq.setTotal_calcium(BigDecimal.ZERO); + summaryReq.setTotal_phosphorus(BigDecimal.ZERO); + summaryReq.setTotal_iron(BigDecimal.ZERO); + summaryReq.setTotal_vitamin_a(BigDecimal.ZERO); + summaryReq.setTotal_vitamin_b1(BigDecimal.ZERO); + summaryReq.setTotal_vitamin_b2(BigDecimal.ZERO); + summaryReq.setTotal_niacin(BigDecimal.ZERO); + summaryReq.setTotal_vitamin_c(BigDecimal.ZERO); + summaryReq.setBreakfast_calories(BigDecimal.ZERO); + summaryReq.setLunch_calories(BigDecimal.ZERO); + summaryReq.setDinner_calories(BigDecimal.ZERO); + summaryReq.setSnack_calories(BigDecimal.ZERO); + } + + /** + * 增加营养值 + */ + private void addNutritionValues(DailyNutritionSummaryReq summaryReq, FoodItems foodItem) { + // 获取食物的份量,如果为空则默认为1 + BigDecimal size = foodItem.getSize(); + if (size == null) { + size = BigDecimal.ONE; + } + + // 总卡路里 + if (foodItem.getKcal() != null) { + BigDecimal calories = foodItem.getKcal(); + // 根据份量调整卡路里 + // calories = calories.multiply(size).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + + summaryReq.setTotal_calories(summaryReq.getTotal_calories().add(calories)); + + // 根据餐点类型更新对应的卡路里 + String meal = foodItem.getMeal(); + if (meal != null) { + if ("breakfast".equalsIgnoreCase(meal)) { + summaryReq.setBreakfast_calories(summaryReq.getBreakfast_calories().add(calories)); + } else if ("lunch".equalsIgnoreCase(meal)) { + summaryReq.setLunch_calories(summaryReq.getLunch_calories().add(calories)); + } else if ("dinner".equalsIgnoreCase(meal)) { + summaryReq.setDinner_calories(summaryReq.getDinner_calories().add(calories)); + } else if ("snack".equalsIgnoreCase(meal)) { + summaryReq.setSnack_calories(summaryReq.getSnack_calories().add(calories)); + } + } + } + + // 其他营养素 + if (foodItem.getProtein() != null) { + BigDecimal protein = foodItem.getProtein(); + // 根据份量调整蛋白质 + // protein = protein.multiply(size).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + + summaryReq.setTotal_protein(summaryReq.getTotal_protein().add(protein)); + } + + // 其他营养素也类似处理... + + if (foodItem.getFat() != null) { + summaryReq.setTotal_fat(summaryReq.getTotal_fat().add(foodItem.getFat())); + } + if (foodItem.getCarbo() != null) { + summaryReq.setTotal_carbs(summaryReq.getTotal_carbs().add(foodItem.getCarbo())); + } + if (foodItem.getSodium() != null) { + summaryReq.setTotal_sodium(summaryReq.getTotal_sodium().add(foodItem.getSodium())); + } + if (foodItem.getpotassium() != null) { + summaryReq.setTotal_potassium(summaryReq.getTotal_potassium().add(foodItem.getpotassium())); + } + if (foodItem.getcalcium() != null) { + summaryReq.setTotal_calcium(summaryReq.getTotal_calcium().add(foodItem.getcalcium())); + } + if (foodItem.getphosphorus() != null) { + summaryReq.setTotal_phosphorus(summaryReq.getTotal_phosphorus().add(foodItem.getphosphorus())); + } + if (foodItem.getiron() != null) { + summaryReq.setTotal_iron(summaryReq.getTotal_iron().add(foodItem.getiron())); + } + if (foodItem.getvitaminA() != null) { + summaryReq.setTotal_vitamin_a(summaryReq.getTotal_vitamin_a().add(foodItem.getvitaminA())); + } + if (foodItem.getvitaminB1() != null) { + summaryReq.setTotal_vitamin_b1(summaryReq.getTotal_vitamin_b1().add(foodItem.getvitaminB1())); + } + if (foodItem.getvitaminB2() != null) { + summaryReq.setTotal_vitamin_b2(summaryReq.getTotal_vitamin_b2().add(foodItem.getvitaminB2())); + } + if (foodItem.getniacin() != null) { + summaryReq.setTotal_niacin(summaryReq.getTotal_niacin().add(foodItem.getniacin())); + } + if (foodItem.getvitaminC() != null) { + summaryReq.setTotal_vitamin_c(summaryReq.getTotal_vitamin_c().add(foodItem.getvitaminC())); + } + } + + /** + * 重新计算营养汇总数据 + */ + private void recalculateNutritionSummary(DailyNutritionSummaryReq summaryReq, Long userId, LocalDate date) { + // 查询当天该用户的所有食物项 + Map args = new HashMap<>(); + args.put("userId", userId); + Date foodDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()); + args.put("date", foodDate); + List> foodItems = search(args); + + // 保存日期和用户ID字段 + Date originalDate = summaryReq.getDate(); + Long originalUserId = summaryReq.getUser_id(); + + // 重置所有营养值 + initializeNutritionValues(summaryReq); + + // 恢复日期字段 + if (originalDate == null) { + // 如果原始日期为空,使用传入的日期 + summaryReq.setDate(foodDate); + } else { + // 否则恢复原始日期 + summaryReq.setDate(originalDate); + } + + // 恢复用户ID字段 + if (originalUserId == null) { + // 如果原始用户ID为空,使用传入的用户ID + summaryReq.setUser_id(userId); + } else { + // 否则恢复原始用户ID + summaryReq.setUser_id(originalUserId); + } + + // 遍历所有食物项,累加营养值 + for (Map item : foodItems) { + FoodItems tempItem = createTempFoodItem(item); + if (tempItem != null) { + addNutritionValues(summaryReq, tempItem); + } + } + } + + /** + * 从Map创建临时FoodItems对象 + */ + private FoodItems createTempFoodItem(Map item) { + FoodItems tempItem = new FoodItems(); + + // 获取食物的份量 + BigDecimal size = (BigDecimal) item.get("size"); + + // 设置基本属性 + tempItem.setKcal((BigDecimal) item.get("kcal")); + tempItem.setProtein((BigDecimal) item.get("protein")); + tempItem.setFat((BigDecimal) item.get("fat")); + tempItem.setCarbo((BigDecimal) item.get("carbo")); + tempItem.setSodium((BigDecimal) item.get("sodium")); + tempItem.setpotassium((BigDecimal) item.get("potassium")); + tempItem.setcalcium((BigDecimal) item.get("calcium")); + tempItem.setphosphorus((BigDecimal) item.get("phosphorus")); + tempItem.setiron((BigDecimal) item.get("iron")); + tempItem.setvitaminA((BigDecimal) item.get("vitaminA")); + tempItem.setvitaminB1((BigDecimal) item.get("vitaminB1")); + tempItem.setvitaminB2((BigDecimal) item.get("vitaminB2")); + tempItem.setniacin((BigDecimal) item.get("niacin")); + tempItem.setvitaminC((BigDecimal) item.get("vitaminC")); + tempItem.setMeal((String) item.get("meal")); + tempItem.setSize(size); + + return tempItem; + } + + /** + * 判断两个日期是否是同一天 + */ + private boolean isSameDay(Date date1, Date date2) { + if (date1 == null || date2 == null) { + return false; + } + + // 安全地转换日期为 LocalDate + LocalDate localDate1 = convertToLocalDate(date1); + LocalDate localDate2 = convertToLocalDate(date2); + + return localDate1.equals(localDate2); + } + + /** + * 安全地将 Date 转换为 LocalDate + */ + private LocalDate convertToLocalDate(Date date) { + if (date instanceof java.sql.Date) { + // 处理 java.sql.Date + return ((java.sql.Date) date).toLocalDate(); + } else { + // 处理 java.util.Date + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/foodItems/web/FoodItemsController.java b/src/main/java/com/ffii/fhsmsc/modules/foodItems/web/FoodItemsController.java new file mode 100644 index 0000000..1e1d49d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/foodItems/web/FoodItemsController.java @@ -0,0 +1,69 @@ +package com.ffii.fhsmsc.modules.foodItems.web; + +import java.util.Map; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.foodItems.req.SaveFoodItemsReq; +import com.ffii.fhsmsc.modules.foodItems.service.FoodItemsService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/foodItems") +public class FoodItemsController { + private FoodItemsService foodItemsService; + + public FoodItemsController( + FoodItemsService foodItemsService + ) { + this.foodItemsService = foodItemsService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(foodItemsService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @GetMapping("/listByDateRange") + public RecordsRes> listByDateRange( + @RequestParam("startDate") String startDateStr, + @RequestParam("endDate") String endDateStr, + @RequestParam("userId") Integer userId) { + + // 将字符串日期解析为LocalDate + DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE; + LocalDate startDate = LocalDate.parse(startDateStr, formatter); + LocalDate endDate = LocalDate.parse(endDateStr, formatter); + + // 调用服务层方法获取数据 + List> results = foodItemsService.searchByDateRange(startDate, endDate, userId); + + return new RecordsRes<>(results); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid SaveFoodItemsReq req) { + return new IdRes(foodItemsService.saveOrUpdate(req).getId()); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impact.java b/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impact.java new file mode 100644 index 0000000..a82bd5d --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impact.java @@ -0,0 +1,30 @@ +package com.ffii.fhsmsc.modules.nutrient_impact.enity; + + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "nutrient_impact") +public class nutrient_impact extends BaseEntity { + + @NotBlank + @Column + private String nutrient_name; + + @Column + private String nutrient_impact; + + @Column + private String organ_affected; + + + @Column + + private String impact_description; +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impactRepository.java b/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impactRepository.java new file mode 100644 index 0000000..28b6a20 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrient_impact/enity/nutrient_impactRepository.java @@ -0,0 +1,5 @@ +package com.ffii.fhsmsc.modules.nutrient_impact.enity; + +public class nutrient_impactRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_need.java b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_need.java new file mode 100644 index 0000000..050560f --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_need.java @@ -0,0 +1,94 @@ +package com.ffii.fhsmsc.modules.nutrients_need.enity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "nutrients_need") +public class Nutrients_need extends BaseEntity { + + @NotBlank + @Column + private int id; + + @Column + private BigDecimal bmr; + + @Column + private BigDecimal tdee; + + @Column + private BigDecimal fat_need; + + @Column + private BigDecimal protein_need; + + @Column + private BigDecimal carbo_need; + + @Column + private LocalDateTime last_updated; + + // getter & setter + + public int getid() { + return id; + } + + public void setid(int id) { + this.id = id; + } + public BigDecimal getbmr() { + return bmr; + } + public void setbmr(BigDecimal bmr) { + this.bmr = bmr; + } + public BigDecimal gettdee() { + return tdee; + } + public void settdee(BigDecimal tdee) { + this.tdee = tdee; + } + public BigDecimal getfat_need() { + return fat_need; + } + public void setfat_need(BigDecimal fat_need) { + this.fat_need = fat_need; + } + public BigDecimal getprotein_need() { + return protein_need; + } + public void setprotein_need(BigDecimal protein_need) { + this.protein_need = protein_need; + } + public BigDecimal getcarbo_need() { + return carbo_need; + } + public void setcarbo_need(BigDecimal carbo_need) { + this.carbo_need = carbo_need; + } + public LocalDateTime getlast_updated() { + return last_updated; + } + public void setlast_updated(LocalDateTime last_updated) { + this.last_updated = last_updated; + } + + + + + + + + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_needRepository.java b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_needRepository.java new file mode 100644 index 0000000..d26d436 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/enity/Nutrients_needRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.nutrients_need.enity; +import com.ffii.core.support.AbstractRepository; + +public interface Nutrients_needRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/req/SaveNutrients_needReq.java b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/req/SaveNutrients_needReq.java new file mode 100644 index 0000000..edf0e50 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/req/SaveNutrients_needReq.java @@ -0,0 +1,57 @@ +package com.ffii.fhsmsc.modules.nutrients_need.req; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class SaveNutrients_needReq { + private Long id; + private BigDecimal bmr; + private BigDecimal tdee; + private BigDecimal fat_need; + private BigDecimal protein_need; + private BigDecimal carbo_need; + private LocalDateTime last_updated; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public BigDecimal getBmr() { + return bmr; + } + public void setBmr(BigDecimal bmr) { + this.bmr = bmr; + } + public BigDecimal getTdee() { + return tdee; + } + public BigDecimal getFat_need() { + return fat_need; + } + public void setFat_need(BigDecimal fat_need) { + this.fat_need = fat_need; + } + public BigDecimal getProtein_need() { + return protein_need; + } + public void setProtein_need(BigDecimal protein_need) { + this.protein_need = protein_need; + } + public BigDecimal getCarbo_need() { + return carbo_need; + } + public void setCarbo_need(BigDecimal carbo_need) { + this.carbo_need = carbo_need; + } + public LocalDateTime getLast_updated() { + return last_updated; + } + public void setLast_updated(LocalDateTime last_updated) { + this.last_updated = last_updated; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/service/Nutrients_needService.java b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/service/Nutrients_needService.java new file mode 100644 index 0000000..0e2245b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/service/Nutrients_needService.java @@ -0,0 +1,63 @@ +package com.ffii.fhsmsc.modules.nutrients_need.service; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.nutrients_need.enity.Nutrients_need; +import com.ffii.fhsmsc.modules.nutrients_need.enity.Nutrients_needRepository; +import com.ffii.fhsmsc.modules.nutrients_need.req.SaveNutrients_needReq; + +import jakarta.validation.Valid; +@Service +public class Nutrients_needService extends AbstractBaseEntityService { + public Nutrients_needService(JdbcDao jdbcDao, Nutrients_needRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " fi.*" + + " FROM `nutrients_need` fi" + + " WHERE 1=1"); + + // if (args != null) { + // if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + // if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + // if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + // if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + // } + if (args != null) { + if (args.containsKey("user_Id")) { + sql.append(" AND fi.id = :user_Id"); // 使用 id 而不是 user_id + } + } + sql.append(" ORDER BY fi.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public Nutrients_need saveOrUpdate(@Valid SaveNutrients_needReq req) { + Nutrients_need instance; + + if (req.getId() != null) { + if(req.getId()>0){ + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new Nutrients_need(); + } + } else { + instance = new Nutrients_need(); + } + BeanUtils.copyProperties(req, instance); + saveAndFlush(instance); + + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/web/Nutrients_needController.java b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/web/Nutrients_needController.java new file mode 100644 index 0000000..4a4b65c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/nutrients_need/web/Nutrients_needController.java @@ -0,0 +1,48 @@ +package com.ffii.fhsmsc.modules.nutrients_need.web; +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.nutrients_need.req.SaveNutrients_needReq; +import com.ffii.fhsmsc.modules.nutrients_need.service.Nutrients_needService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/nutrients_need") +public class Nutrients_needController { + private Nutrients_needService nutrients_needService; + + public Nutrients_needController( + Nutrients_needService nutrients_needService + ) { + this.nutrients_needService = nutrients_needService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(nutrients_needService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger("user_Id") + // .addInteger(Params.ID) + // .addStringLike(Params.NAME) + // .addStringLike("description") + // .addInteger("userId") + .build())); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid SaveNutrients_needReq req) { + return new IdRes(nutrients_needService.saveOrUpdate(req).getId()); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/settings/entity/Settings.java b/src/main/java/com/ffii/fhsmsc/modules/settings/entity/Settings.java new file mode 100644 index 0000000..0496734 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/settings/entity/Settings.java @@ -0,0 +1,72 @@ +package com.ffii.fhsmsc.modules.settings.entity; + +import com.ffii.core.entity.IdEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; + +@Entity +@Table(name = "settings") +public class Settings extends IdEntity { + public static String TYPE_STRING = "string"; + public static String TYPE_INT = "integer"; + public static String TYPE_FLOAT = "float"; + public static String TYPE_BOOLEAN = "boolean"; + public static String TYPE_DATE = "date"; + public static String TYPE_TIME = "time"; + public static String TYPE_DATETIME = "datetime"; + // other "A/B" value must "A" or "B" + + //lowercase + public static String VALUE_BOOLEAN_TRUE = "true"; + public static String VALUE_BOOLEAN_FALSE = "false"; + + @NotNull + @Column + private String name; + + @NotNull + @Column + private String value; + + @Column + private String category; + + @Column + private String type; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/settings/entity/SettingsRepository.java b/src/main/java/com/ffii/fhsmsc/modules/settings/entity/SettingsRepository.java new file mode 100644 index 0000000..5b33d5c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/settings/entity/SettingsRepository.java @@ -0,0 +1,12 @@ +package com.ffii.fhsmsc.modules.settings.entity; + +import java.util.Optional; + +import org.springframework.data.repository.query.Param; + +import com.ffii.core.support.AbstractRepository; + +public interface SettingsRepository extends AbstractRepository { + + Optional findByName(@Param("name") String name); +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/settings/service/SettingsService.java b/src/main/java/com/ffii/fhsmsc/modules/settings/service/SettingsService.java new file mode 100644 index 0000000..4075ddb --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/settings/service/SettingsService.java @@ -0,0 +1,219 @@ +package com.ffii.fhsmsc.modules.settings.service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractIdEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.settings.entity.Settings; +import com.ffii.fhsmsc.modules.settings.entity.SettingsRepository; + + +@Service +public class SettingsService extends AbstractIdEntityService { + + public SettingsService(JdbcDao jdbcDao, SettingsRepository repository) { + super(jdbcDao, repository); + } + + public Optional findByName(String name) { + return this.repository.findByName(name); + } + + public boolean validateType(String type, String value) { + if (StringUtils.isBlank(type)) return true; + + if (Settings.TYPE_STRING.equals(type)) return true; + + if (Settings.TYPE_BOOLEAN.equals(type)) { + return Settings.VALUE_BOOLEAN_TRUE.equals(value) || Settings.VALUE_BOOLEAN_FALSE.equals(value); + } + + if (Settings.TYPE_INT.equals(type)) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + if (Settings.TYPE_FLOAT.equals(type)) { + try { + Float.parseFloat(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + if (Settings.TYPE_DATE.equals(type)) { + try { + LocalDate.parse(value, DateTimeFormatter.ISO_DATE); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + if (Settings.TYPE_TIME.equals(type)) { + try { + LocalTime.parse(value, DateTimeFormatter.ISO_TIME); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + if (Settings.TYPE_DATETIME.equals(type)) { + try { + LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + + if (StringUtils.indexOf(type, "/") >= 0) { + for (String t : type.split("/")) { + if (t.equals(value)) return true; + } + return false; + } + + return false; + } + + @Transactional(rollbackFor = Exception.class) + public void update(String name, String value) { + Settings settings = this.findByName(name) + .orElseThrow(InternalServerErrorException::new); + if (!validateType(settings.getType(), value)) { + throw new InternalServerErrorException(); + } + settings.setValue(value); + this.save(settings); + } + + @Transactional(rollbackFor = Exception.class) + public void update(String name, LocalDate date) { + this.update(name, date.format(DateTimeFormatter.ISO_DATE)); + } + + @Transactional(rollbackFor = Exception.class) + public void update(String name, LocalDateTime datetime) { + this.update(name, datetime.format(DateTimeFormatter.ISO_DATE_TIME)); + } + + @Transactional(rollbackFor = Exception.class) + public void update(String name, LocalTime time) { + this.update(name, time.format(DateTimeFormatter.ISO_TIME)); + } + + public String getString(String name) { + return this.findByName(name) + .map(Settings::getValue) + .orElseThrow(InternalServerErrorException::new); + } + + public int getInt(String name) { + return this.findByName(name) + .map(Settings::getValue) + .map(v -> { + try { + return Integer.parseInt(v); + } catch (final NumberFormatException nfe) { + return null; + } + }) + .orElseThrow(InternalServerErrorException::new); + } + + public double getDouble(String name) { + return this.findByName(name) + .map(Settings::getValue) + .map(v -> { + try { + return Double.parseDouble(v); + } catch (final NumberFormatException nfe) { + return null; + } + }) + .orElseThrow(InternalServerErrorException::new); + } + + public boolean getBoolean(String name) { + return this.findByName(name) + .map(Settings::getValue) + .map(Settings.VALUE_BOOLEAN_TRUE::equals) + .orElseThrow(InternalServerErrorException::new); + } + + public LocalDate getDate(String name) { + return this.getDate(name, DateTimeFormatter.ISO_DATE); + } + + private LocalDate getDate(String name, DateTimeFormatter formatter) { + return this.findByName(name) + .map(Settings::getValue) + .map(v -> { + try { + return LocalDate.parse(v, formatter); + } catch (DateTimeParseException e) { + return null; + } + }) + .orElseThrow(InternalServerErrorException::new); + } + + public LocalDateTime getDatetime(String name) { + return this.getDatetime(name, DateTimeFormatter.ISO_DATE_TIME); + } + + private LocalDateTime getDatetime(String name, DateTimeFormatter formatter) { + return this.findByName(name) + .map(Settings::getValue) + .map(v -> { + try { + return LocalDateTime.parse(v, formatter); + } catch (DateTimeParseException e) { + return null; + } + }) + .orElseThrow(InternalServerErrorException::new); + } + + public LocalTime getTime(String name) { + return this.getTime(name, DateTimeFormatter.ISO_TIME); + } + + private LocalTime getTime(String name, DateTimeFormatter formatter) { + return this.findByName(name) + .map(Settings::getValue) + .map(v -> { + try { + return LocalTime.parse(v, formatter); + } catch (DateTimeParseException e) { + return null; + } + }) + .orElseThrow(InternalServerErrorException::new); + } + + public List> listSetting(Map args){ + StringBuilder sql = new StringBuilder("SELECT" + + " * " + + " FROM settings s " + + " WHERE s.category = :category " + ); + return jdbcDao.queryForList(sql.toString(), args); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/settings/web/SettingsController.java b/src/main/java/com/ffii/fhsmsc/modules/settings/web/SettingsController.java new file mode 100644 index 0000000..0a0dc1c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/settings/web/SettingsController.java @@ -0,0 +1,132 @@ +package com.ffii.fhsmsc.modules.settings.web; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.exception.BadRequestException; +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.Params; +import com.ffii.fhsmsc.modules.settings.entity.Settings; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +@RestController +@RequestMapping("/settings") +public class SettingsController{ + + private SettingsService settingsService; + + public SettingsController(SettingsService settingsService) { + this.settingsService = settingsService; + } + + @Operation(summary = "list system settings", security = @SecurityRequirement(name = "bearerAuth")) + @GetMapping + // @PreAuthorize("hasAuthority('ADMIN')") + public List listAll() { + return this.settingsService.listAll(); + } + + @Operation(summary = "update system setting", security = @SecurityRequirement(name = "bearerAuth")) + @PatchMapping("/{name}") + // @PreAuthorize("hasAuthority('ADMIN')") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void update(@PathVariable String name, @RequestBody @Valid UpdateReq body) { + Settings entity = this.settingsService.findByName(name) + .orElseThrow(NotFoundException::new); + if (!this.settingsService.validateType(entity.getType(), body.value)) { + throw new BadRequestException(); + } + + entity.setValue(body.value); + this.settingsService.save(entity); + } + + @GetMapping("/{name}") + public Map getSetting(@PathVariable String name) { + Settings entity = this.settingsService.findByName(name) + .orElseThrow(NotFoundException::new); + + return Map.of( + Params.DATA, entity.getValue() + ); + } + + @PutMapping("/save") + // @PreAuthorize("hasAuthority('ADMIN')") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void update(@RequestBody @Valid UpdateListReq req) { + for (Map setting : req.getSettingList()) { + Settings entity = this.settingsService.findByName(setting.get("name")) + .orElseThrow(NotFoundException::new); + + if (!this.settingsService.validateType(entity.getType(), setting.get("value"))) { + throw new BadRequestException(); + } + + entity.setValue(setting.get("value")); + this.settingsService.save(entity); + } + } + + + @GetMapping("/config") + public RecordsRes> getSystemConfig(HttpServletRequest request) throws ServletRequestBindingException { + System.out.println(request); + return new RecordsRes<>(settingsService.listSetting( + Map.of("category","settings") + )); + } + + @GetMapping("/passwordPolicy") + public RecordsRes> getPasswordConfig(HttpServletRequest request) throws ServletRequestBindingException { + System.out.println(request); + return new RecordsRes<>(settingsService.listSetting( + Map.of("category","passwordPolicy") + )); + } + + public static class UpdateListReq { + @NotNull + private List> settingList; + + public List> getSettingList() { + return settingList; + } + + public void setSettingList(List> settingList) { + this.settingList = settingList; + } + } + + public static class UpdateReq { + @NotBlank + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/Group.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/Group.java new file mode 100644 index 0000000..714ee3b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/Group.java @@ -0,0 +1,37 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; + +import com.ffii.core.entity.BaseEntity; + +@Entity +@Table(name = "`group`") +public class Group extends BaseEntity { + + @NotNull + @Column + private String name; + + @Column + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/GroupRepository.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/GroupRepository.java new file mode 100644 index 0000000..383b753 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/GroupRepository.java @@ -0,0 +1,6 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface GroupRepository extends AbstractRepository { +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/User.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/User.java new file mode 100644 index 0000000..e6c5577 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/User.java @@ -0,0 +1,262 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import java.time.LocalDate; +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.validation.constraints.NotBlank; + +/** @author Terence */ +@Entity +@Table(name = "user") +public class User extends BaseEntity implements UserDetails { + + @NotBlank + //@Column(unique = true) + @Column + private String username; + + @JsonIgnore + @Column + private String password; + + // @NotNull + @Column + private Boolean locked = Boolean.FALSE; + + @NotBlank + @Column + private String name; + + @Column + private LocalDate expiryDate; + + @JsonIgnore + @Transient + private Collection authorities; + + @Column + private String locale; + + @Column + private String fullname; + + @Column + private String firstname; + + @Column + private String lastname; + + @Column + private String department; + + @Column + private String title; + + @Column + private String email; + + @Column + private String phone1; + + @Column + private String phone2; + + @Column + private String remarks; + + @Column + private String post; + + @Column + private boolean has_information; + + public boolean isLocked() { + return this.locked == null ? false : this.locked; + } + + // getter & setter + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public Boolean getLocked() { + return locked; + } + + public void setLocked(Boolean locked) { + this.locked = locked; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDate getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(LocalDate expiryDate) { + this.expiryDate = expiryDate; + } + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone1() { + return phone1; + } + + public void setPhone1(String phone1) { + this.phone1 = phone1; + } + + public String getPhone2() { + return phone2; + } + + public void setPhone2(String phone2) { + this.phone2 = phone2; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + // override + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return this.getExpiryDate() == null || this.getExpiryDate().isAfter(LocalDate.now()); + } + + @Override + public boolean isAccountNonLocked() { + return !this.isLocked(); + } + + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @JsonIgnore + @Override + public boolean isEnabled() { + return true; + } + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + + public String getPost() { + return this.post; + } + + public void setPost(String post) { + this.post = post; + } + + public boolean getHas_information() { + return has_information; + } + + public void setHas_information(boolean has_information) { + this.has_information = has_information; + } + + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistory.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistory.java new file mode 100644 index 0000000..ed7d77b --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistory.java @@ -0,0 +1,52 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Jason Lam */ +@Entity +@Table(name = "user_password_history") +public class UserPasswordHistory extends BaseEntity { + + @Column + private Long userId; + + @Column + private LocalDateTime date; + + @JsonIgnore + @Column + private String password; + + public Long getUserId() { + return this.userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public LocalDateTime getDate() { + return this.date; + } + + public void setDate(LocalDateTime date) { + this.date = date; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistoryRepository.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistoryRepository.java new file mode 100644 index 0000000..4d635eb --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserPasswordHistoryRepository.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface UserPasswordHistoryRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserRepository.java b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserRepository.java new file mode 100644 index 0000000..28444ca --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/entity/UserRepository.java @@ -0,0 +1,15 @@ +package com.ffii.fhsmsc.modules.user.entity; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.repository.query.Param; + +import com.ffii.core.support.AbstractRepository; + +public interface UserRepository extends AbstractRepository { + + List findByName(@Param("name") String name); + + Optional findByUsernameAndDeletedFalse(String username); +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/NewFhsmsCUserReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewFhsmsCUserReq.java new file mode 100644 index 0000000..070b719 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewFhsmsCUserReq.java @@ -0,0 +1,234 @@ +package com.ffii.fhsmsc.modules.user.req; + +import java.time.LocalDate; +import java.util.List; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** @author Jason */ +public class NewFhsmsCUserReq { + + private Long id; + + @NotNull + private Boolean locked; + + @Size(max = 30) + @NotBlank + @Pattern(regexp = "^[A-Za-z0-9]+$") + private String username; + + private String password; + + private String name; + + @NotBlank + private String fullname; + private String firstname; + private String lastname; + private String title; + private String department; + private String phone1; + private String phone2; + private String post; + private LocalDate expiryDate; + private String locale; + private String remarks; + + @NotBlank + @Email + private String email; + + // @NotNull + private List addGroupIds; + // @NotNull + private List removeGroupIds; + + // @NotNull + private List addAuthIds; + // @NotNull + private List removeAuthIds; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Boolean getLocked() { + return locked; + } + + public void setLocked(Boolean locked) { + this.locked = locked; + } + + public LocalDate getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(LocalDate expiryDate) { + this.expiryDate = expiryDate; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstName(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + + public List getAddGroupIds() { + return this.addGroupIds; + } + + public void setAddGroupIds(List addGroupIds) { + this.addGroupIds = addGroupIds; + } + + public List getRemoveGroupIds() { + return this.removeGroupIds; + } + + public void setRemoveGroupIds(List removeGroupIds) { + this.removeGroupIds = removeGroupIds; + } + + public List getAddAuthIds() { + return this.addAuthIds; + } + + public void setAddAuthIds(List addAuthIds) { + this.addAuthIds = addAuthIds; + } + + public List getRemoveAuthIds() { + return this.removeAuthIds; + } + + public void setRemoveAuthIds(List removeAuthIds) { + this.removeAuthIds = removeAuthIds; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + + public Boolean isLocked() { + return this.locked; + } + + public String getFullname() { + return this.fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getPhone1() { + return this.phone1; + } + + public void setPhone1(String phone1) { + this.phone1 = phone1; + } + + public String getPhone2() { + return this.phone2; + } + + public void setPhone2(String phone2) { + this.phone2 = phone2; + } + + public String getPost() { + return this.post; + } + + public void setPost(String post) { + this.post = post; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/NewPublicUserReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewPublicUserReq.java new file mode 100644 index 0000000..65c769e --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewPublicUserReq.java @@ -0,0 +1,29 @@ +package com.ffii.fhsmsc.modules.user.req; + +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** @author Alex */ +public class NewPublicUserReq extends UpdateUserReq { + + @Size(max = 30) + @Pattern(regexp = "^[A-Za-z0-9]+$") + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/NewUserReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewUserReq.java new file mode 100644 index 0000000..d00ad85 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/NewUserReq.java @@ -0,0 +1,21 @@ +package com.ffii.fhsmsc.modules.user.req; + +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** @author Alex */ +public class NewUserReq extends UpdateUserReq { + + @Size(max = 30) + @Pattern(regexp = "^[A-Za-z0-9]+$") + private String username; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/SaveGroupReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/SaveGroupReq.java new file mode 100644 index 0000000..95bbcb4 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/SaveGroupReq.java @@ -0,0 +1,80 @@ +package com.ffii.fhsmsc.modules.user.req; + +import java.util.List; + +import jakarta.validation.constraints.NotNull; + +public class SaveGroupReq { + private Long id; + + @NotNull + private String name; + private String description; + + @NotNull + private List addUserIds; + @NotNull + private List removeUserIds; + + @NotNull + private List addAuthIds; + @NotNull + private List removeAuthIds; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getAddUserIds() { + return addUserIds; + } + + public void setAddUserIds(List addUserIds) { + this.addUserIds = addUserIds; + } + + public List getRemoveUserIds() { + return removeUserIds; + } + + public void setRemoveUserIds(List removeUserIds) { + this.removeUserIds = removeUserIds; + } + + public List getAddAuthIds() { + return addAuthIds; + } + + public void setAddAuthIds(List addAuthIds) { + this.addAuthIds = addAuthIds; + } + + public List getRemoveAuthIds() { + return removeAuthIds; + } + + public void setRemoveAuthIds(List removeAuthIds) { + this.removeAuthIds = removeAuthIds; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/SearchUserReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/SearchUserReq.java new file mode 100644 index 0000000..b7ad5e2 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/SearchUserReq.java @@ -0,0 +1,110 @@ +package com.ffii.fhsmsc.modules.user.req; + +public class SearchUserReq { + private Integer id; + private Integer groupId; + private String username; + private String name; + private Boolean locked; + + private Integer start; + private Integer limit; + + private String fullname; + private String post; + private String email; + private String phone; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getStart() { + return start; + } + + public void setStart(Integer start) { + this.start = start; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } + + public Boolean getLocked() { + return locked; + } + + public void setLocked(Boolean locked) { + this.locked = locked; + } + + public Boolean isLocked() { + return this.locked; + } + + public String getFullname() { + return this.fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getPost() { + return this.post; + } + + public void setPost(String post) { + this.post = post; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return this.phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/req/UpdateUserReq.java b/src/main/java/com/ffii/fhsmsc/modules/user/req/UpdateUserReq.java new file mode 100644 index 0000000..2bc9844 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/req/UpdateUserReq.java @@ -0,0 +1,151 @@ +package com.ffii.fhsmsc.modules.user.req; + +import java.time.LocalDate; +import java.util.List; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** @author Alex */ +public class UpdateUserReq { + + @NotNull + private Boolean locked; + + @Size(max = 90) + @NotBlank + private String name; + + private String firstname; + private String lastname; + private LocalDate expiryDate; + private String locale; + private String remarks; + + @NotBlank + @Email + private String email; + @NotBlank + private String department; + + // @NotNull + private List addGroupIds; + // @NotNull + private List removeGroupIds; + + // @NotNull + private List addAuthIds; + // @NotNull + private List removeAuthIds; + + public Boolean getLocked() { + return locked; + } + + public void setLocked(Boolean locked) { + this.locked = locked; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDate getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(LocalDate expiryDate) { + this.expiryDate = expiryDate; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstName(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public List getAddGroupIds() { + return addGroupIds; + } + + public void setAddGroupIds(List addGroupIds) { + this.addGroupIds = addGroupIds; + } + + public List getRemoveGroupIds() { + return removeGroupIds; + } + + public void setRemoveGroupIds(List removeGroupIds) { + this.removeGroupIds = removeGroupIds; + } + + public List getAddAuthIds() { + return addAuthIds; + } + + public void setAddAuthIds(List addAuthIds) { + this.addAuthIds = addAuthIds; + } + + public List getRemoveAuthIds() { + return removeAuthIds; + } + + public void setRemoveAuthIds(List removeAuthIds) { + this.removeAuthIds = removeAuthIds; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/GroupService.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/GroupService.java new file mode 100644 index 0000000..a4fc381 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/GroupService.java @@ -0,0 +1,352 @@ +package com.ffii.fhsmsc.modules.user.service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.core.utils.JsonUtils; +import com.ffii.core.utils.Params; +import com.ffii.fhsmsc.modules.common.SecurityUtils; +import com.ffii.fhsmsc.modules.common.service.AuditLogService; +import com.ffii.fhsmsc.modules.user.entity.Group; +import com.ffii.fhsmsc.modules.user.entity.GroupRepository; +import com.ffii.fhsmsc.modules.user.req.SaveGroupReq; + +import jakarta.persistence.Table; +import jakarta.validation.Valid; + + +@Service +public class GroupService extends AbstractBaseEntityService { + + @Autowired + private AuditLogService auditLogService; + + public GroupService(JdbcDao jdbcDao, GroupRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " g.*" + + " FROM `group` g" + + " WHERE g.deleted = FALSE"); + + if (args != null) { + if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + if (args.containsKey(Params.NAME)) sql.append(" AND g.name LIKE :name"); + if (args.containsKey("description")) sql.append(" AND g.description LIKE :description"); + } + + sql.append(" ORDER BY g.name"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public Map getAuditLogObject(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " g.id, " + + " g.created, " + + " g.createdBy AS createdById, " + + " u1.name AS createdBy, " + + " g.version, " + + " g.modified, " + + " g.modifiedBy AS modifiedById, " + + " u2.name AS modifiedBy, " + + " g.deleted, " + + " g.name, " + + " g.description " + + " FROM `group` g " + + " LEFT JOIN `user` u1 ON u1.id = g.createdBy " + + " LEFT JOIN `user` u2 ON u2.id = g.modifiedBy " + + " WHERE g.id = :id; " + ); + + return jdbcDao.queryForMap(sql.toString(), req).get(); + } + + public List getUserAuthIds(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " ga.authId " + + " FROM group_authority ga " + + " WHERE ga.groupId = :id; " + ); + + return jdbcDao.queryForInts(sql.toString(), req); + } + + public List getUserAuthNames(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " a.name " + + " FROM group_authority ga " + + " LEFT JOIN authority a ON ga.authId = a.id " + + " WHERE ga.groupId = :id; " + ); + + return jdbcDao.queryForStrings(sql.toString(), req); + } + + public List getUserGroupIds(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " ug.userId " + + " FROM user_group ug " + + " WHERE ug.groupId = :id; " + ); + + return jdbcDao.queryForInts(sql.toString(), req); + } + + public List getUserGroupNames(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " u.name " + + " FROM user_group ug " + + " LEFT JOIN `user` u ON u.id = ug.userId " + + " WHERE ug.groupId = :id; " + ); + return jdbcDao.queryForStrings(sql.toString(), req); + } + + public List> searchForCombo(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " g.id," + + " g.name" + + " FROM `group` g" + + " WHERE g.deleted = FALSE"); + + if (args != null) { + if (args.containsKey(Params.QUERY)) sql.append(" AND (g.name LIKE :query)"); + if (args.containsKey(Params.ID)) sql.append(" AND g.id = :id"); + } + + sql.append(" ORDER BY g.name asc"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public void delete(Group instance) { + // =====GET OLD AUDIT LOG=====// + String tableName = instance.getClass().getAnnotation(Table.class).name(); + Map oldValueObject = new HashMap(); + Map newValueObject = new HashMap(); + + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData= getAuditLogObject(input); + logData.put("group_auth", getUserAuthNames(input)); + logData.put("group_auth_id", getUserAuthIds(input)); + logData.put("group_user", getUserGroupNames(input)); + logData.put("group_user_id", getUserGroupIds(input)); + oldValueObject = logData; + } + + // =====GET OLD AUDIT LOG=====// + + Map args = Map.of("groupId", instance.getId()); + jdbcDao.executeUpdate("DELETE FROM user_group WHERE groupId = :groupId;", args); + jdbcDao.executeUpdate("DELETE FROM group_authority WHERE groupId = :groupId;", args); + this.markDelete(instance); + + // =====GET NEW AUDIT LOG=====// + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData= getAuditLogObject(input); + logData.put("group_auth", getUserAuthNames(input)); + logData.put("group_auth_id", getUserAuthIds(input)); + logData.put("group_user", getUserGroupNames(input)); + logData.put("group_user_id", getUserGroupIds(input)); + newValueObject = logData; + } + + + if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 || + auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0 + ){ + auditLogService.save( + tableName, + instance.getId(), + instance.getName(), + SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null, + new Date(), + JsonUtils.toJsonString(auditLogService.compareMaps(newValueObject,oldValueObject)), + JsonUtils.toJsonString(auditLogService.compareMaps(oldValueObject,newValueObject))); + } + // =====GET NEW AUDIT LOG=====// + } + + @Transactional(rollbackFor = Exception.class) + public Group saveOrUpdate(@Valid SaveGroupReq req ) { + Group instance; + + if (req.getId() != null) { + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + } else { + instance = new Group(); + } + BeanUtils.copyProperties(req, instance); + + //=====GET OLD AUDIT LOG=====// + String tableName = instance.getClass().getAnnotation(Table.class).name(); + Map oldValueObject = new HashMap(); + Map newValueObject = new HashMap(); + + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData= getAuditLogObject(input); + logData.put("group_auth", getUserAuthNames(input)); + logData.put("group_auth_id", getUserAuthIds(input)); + logData.put("group_user", getUserGroupNames(input)); + logData.put("group_user_id", getUserGroupIds(input)); + oldValueObject = logData; + } + + //=====GET OLD AUDIT LOG=====// + + instance = saveAndFlush(instance); + Long id = instance.getId(); + + List> userBatchInsertValues = req.getAddUserIds().stream() + .map(userId -> Map.of("groupId", id, "userId", userId)) + .collect(Collectors.toList()); + List> userBatchDeleteValues = req.getRemoveUserIds().stream() + .map(userId -> Map.of("groupId", id, "userId", userId)) + .collect(Collectors.toList()); + + if (!userBatchInsertValues.isEmpty()) { + jdbcDao.batchUpdate( + "INSERT IGNORE INTO user_group (groupId,userId)" + + " VALUES (:groupId, :userId)", + userBatchInsertValues); + } + if (!userBatchDeleteValues.isEmpty()) { + jdbcDao.batchUpdate( + "DELETE FROM user_group" + + " WHERE groupId = :groupId AND userId = :userId", + userBatchDeleteValues); + } + + List> authBatchInsertValues = req.getAddAuthIds().stream() + .map(authId -> Map.of("groupId", id, "authId", authId)) + .collect(Collectors.toList()); + List> authBatchDeleteValues = req.getRemoveAuthIds().stream() + .map(authId -> Map.of("groupId", id, "authId", authId)) + .collect(Collectors.toList()); + + if (!authBatchInsertValues.isEmpty()) { + jdbcDao.batchUpdate( + "INSERT IGNORE INTO group_authority (groupId, authId)" + + " VALUES (:groupId, :authId)", + authBatchInsertValues); + } + if (!authBatchDeleteValues.isEmpty()) { + jdbcDao.batchUpdate( + "DELETE FROM group_authority" + + " WHERE groupId = :groupId AND authId = :authId", + authBatchDeleteValues); + } + + //=====GET NEW AUDIT LOG=====// + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData= getAuditLogObject(input); + logData.put("group_auth", getUserAuthNames(input)); + logData.put("group_auth_id", getUserAuthIds(input)); + logData.put("group_user", getUserGroupNames(input)); + logData.put("group_user_id", getUserGroupIds(input)); + newValueObject = logData; + } + + + if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 || + auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0 + ){ + auditLogService.save( + tableName, + instance.getId(), + instance.getName(), + SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null, + new Date(), + JsonUtils.toJsonString(auditLogService.compareMaps(newValueObject,oldValueObject)), + JsonUtils.toJsonString(auditLogService.compareMaps(oldValueObject,newValueObject))); + } + return instance; + //=====GET NEW AUDIT LOG=====// + } + + public List listGroupAuthId(Long id) { + return jdbcDao.queryForInts( + "SELECT" + + " ga.authId" + + " FROM group_authority ga" + + " WHERE ga.groupId = :id", + Map.of(Params.ID, id)); + } + + public List listGroupUserId(Long id) { + return jdbcDao.queryForInts( + "SELECT" + + " gu.userId" + + " FROM user_group gu" + + " INNER JOIN user u ON u.deleted = FALSE AND gu.userId = u.id" + + " WHERE gu.groupId = :id", + Map.of(Params.ID, id)); + } + + public List> listGroupMember(Map args){ + StringBuilder sql = new StringBuilder("SELECT" + + " u.id, " + + " u.name " + + " FROM user_group ug " + + " LEFT JOIN `user` u on u.id = ug.userId " + + " WHERE ug.groupId = :id " + + " AND u.deleted = FALSE " + ); + + sql.append(" ORDER BY u.id"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + @Transactional(rollbackFor = Exception.class) + public List> listGroupAuth(Map args) { + + StringBuilder sql = new StringBuilder("SELECT" + + " a.id, " + + " a.module," + + " a.authority," + + " a.name," + + " a.description, "); + if (args.containsKey("userId")) + sql.append(" EXISTS(SELECT 1 FROM group_authority WHERE authority = a.authority AND groupId = :groupId) AS v"); + else + sql.append(" 0 AS v"); + sql.append(" FROM authority a" + + " ORDER BY a.module, a.name"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public boolean isNameTaken(String name, Long id) { + StringBuilder sql = new StringBuilder("SELECT" + + " Count(g.id) " + + " FROM `group` g " + + " WHERE g.deleted =FALSE " + + " AND g.name = :name " + + " AND g.id != :id " + ); + + return jdbcDao.queryForBoolean(sql.toString(), Map.of("name", name, "id", id)); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/UserAuthorityService.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserAuthorityService.java new file mode 100644 index 0000000..e49c14c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserAuthorityService.java @@ -0,0 +1,44 @@ +package com.ffii.fhsmsc.modules.user.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.support.AbstractService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.user.entity.User; + +@Service +public class UserAuthorityService extends AbstractService { + private static final String USER_AUTH_SQL = "SELECT a.authority" + + " FROM `user` u" + + " JOIN user_authority ua ON ua.userId = u.id" + + " JOIN authority a ON a.id = ua.authId" + + " WHERE u.deleted = 0" + + " AND u.id = :userId"; + private static final String UNION_SQL = " UNION "; + private static final String GROUP_AUTH_SQL = "SELECT a.authority" + + " FROM `user` u" + + " JOIN user_group ug ON ug.userId = u.id" + + " JOIN `group` g ON g.deleted = 0 AND g.id = ug.groupId" + + " JOIN group_authority ga ON ga.groupId = g.id" + + " JOIN authority a ON a.id = ga.authId" + + " WHERE u.deleted = 0" + + " AND u.id = :userId"; + + public UserAuthorityService(JdbcDao jdbcDao) { + super(jdbcDao); + } + + @Transactional(rollbackFor = Exception.class) + public List>/*Set*/ getUserAuthority(User user) { + List> records = jdbcDao.queryForList(USER_AUTH_SQL + UNION_SQL + GROUP_AUTH_SQL, + Map.of("userId", user.getId())); + + //records.forEach(item -> auths.add(new SimpleGrantedAuthority((String) item.get("authority")))); + return records; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/UserPasswordHistoryService.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserPasswordHistoryService.java new file mode 100644 index 0000000..e12e0f9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserPasswordHistoryService.java @@ -0,0 +1,30 @@ +package com.ffii.fhsmsc.modules.user.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; + +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.fhsmsc.modules.common.SettingNames; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; +import com.ffii.fhsmsc.modules.user.entity.UserPasswordHistory; +import com.ffii.fhsmsc.modules.user.entity.UserPasswordHistoryRepository; + +@Service +public class UserPasswordHistoryService extends AbstractBaseEntityService { + private SettingsService settingsService; + + public UserPasswordHistoryService(JdbcDao jdbcDao, UserPasswordHistoryRepository repository, SettingsService settingsService) { + super(jdbcDao, repository); + this.settingsService = settingsService; + } + + public List getPasswordHistory(long userId) { + int passHist = settingsService.getInt(SettingNames.SYS_PASSWORD_RULE_HISTORY); + StringBuilder sql = new StringBuilder(" SELECT password FROM user_password_history WHERE deleted = 0 AND userId = :userId ORDER BY date DESC LIMIT " + passHist); + return jdbcDao.queryForStrings(sql.toString(), Map.of("userId", userId)); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/UserService.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserService.java new file mode 100644 index 0000000..acebed9 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/UserService.java @@ -0,0 +1,621 @@ +package com.ffii.fhsmsc.modules.user.service; + +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.NoSuchMessageException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.exception.UnprocessableEntityException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.core.utils.JsonUtils; +import com.ffii.core.utils.Params; +import com.ffii.core.utils.PasswordUtils; +import com.ffii.fhsmsc.modules.common.ErrorCodes; +import com.ffii.fhsmsc.modules.common.PasswordRule; +import com.ffii.fhsmsc.modules.common.SecurityUtils; +import com.ffii.fhsmsc.modules.common.service.AuditLogService; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; +import com.ffii.fhsmsc.modules.user.entity.User; +import com.ffii.fhsmsc.modules.user.entity.UserPasswordHistory; +import com.ffii.fhsmsc.modules.user.entity.UserRepository; +import com.ffii.fhsmsc.modules.user.req.NewFhsmsCUserReq; +import com.ffii.fhsmsc.modules.user.req.NewPublicUserReq; +import com.ffii.fhsmsc.modules.user.req.NewUserReq; +import com.ffii.fhsmsc.modules.user.req.SearchUserReq; +import com.ffii.fhsmsc.modules.user.req.UpdateUserReq; +import com.ffii.fhsmsc.modules.user.service.pojo.UserRecord; + +import jakarta.persistence.Table; + +@Service +public class UserService extends AbstractBaseEntityService { + private static final String USER_AUTH_SQL = "SELECT a.authority" + + " FROM `user` u" + + " JOIN user_authority ua ON ua.userId = u.id" + + " JOIN authority a ON a.id = ua.authId" + + " WHERE u.deleted = 0" + + " AND u.id = :userId"; + private static final String UNION_SQL = " UNION "; + private static final String GROUP_AUTH_SQL = "SELECT a.authority" + + " FROM `user` u" + + " JOIN user_group ug ON ug.userId = u.id" + + " JOIN `group` g ON g.deleted = 0 AND g.id = ug.groupId" + + " JOIN group_authority ga ON ga.groupId = g.id" + + " JOIN authority a ON a.id = ga.authId" + + " WHERE u.deleted = 0" + + " AND u.id = :userId"; + + @Autowired + private SettingsService settingsService; + @Autowired + private PasswordEncoder passwordEncoder; + private AuditLogService auditLogService; + private UserPasswordHistoryService userPasswordHistoryService; + + @Autowired + UserRepository userRepository; + + public UserService(JdbcDao jdbcDao, UserRepository userRepository, AuditLogService auditLogService, + UserPasswordHistoryService userPasswordHistoryService, + MessageSource messageSource, RestTemplate restTemplate) { + super(jdbcDao, userRepository); + this.auditLogService = auditLogService; + this.userPasswordHistoryService = userPasswordHistoryService; + } + + public Optional loadUserOptByUsername(String username) { + return findByUsername(username) + .map(user -> { + Set auths = new LinkedHashSet(); + auths.add(new SimpleGrantedAuthority("ROLE_USER")); + jdbcDao.queryForList(USER_AUTH_SQL + UNION_SQL + GROUP_AUTH_SQL, Map.of("userId", user.getId())) + .forEach(item -> auths.add(new SimpleGrantedAuthority((String) item.get("authority")))); + + user.setAuthorities(auths); + return user; + }); + } + + public Optional findByUsername(String username) { + return userRepository.findByUsernameAndDeletedFalse(username); + } + + public Map getAuditLogObject(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " u.id, " + + " u.created, " + + " u.createdBy AS createdById, " + + " u1.name AS createdBy, " + + " u.version, " + + " u.modified, " + + " u.modifiedBy AS modifiedById, " + + " u2.name AS modifiedBy, " + + " u.deleted, " + + " u.username, " + + " u.locked, " + + " u.name , " + + " u.fullname, " + + " u.email, " + + " u.phone1, " + + " u.post " + + " FROM `user` u " + + " LEFT JOIN `user` u1 ON u1.id = u.createdBy " + + " LEFT JOIN `user` u2 ON u2.id = u.modifiedBy " + + " WHERE u.id = :id " + ); + + return jdbcDao.queryForMap(sql.toString(), req).get(); + } + + public List getUserAuthIds(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " ua.authId " + + " FROM user_authority ua " + + " WHERE userId = :id; " + ); + + return jdbcDao.queryForInts(sql.toString(), req); + } + + public List getUserAuthNames(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " a.name " + + " FROM user_authority ua " + + " LEFT JOIN authority a ON ua.authId = a.id " + + " WHERE userId = :id; " + ); + + return jdbcDao.queryForStrings(sql.toString(), req); + } + + public List getUserGroupIds(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " ug.groupId " + + " FROM user_group ug " + + " WHERE userId = :id; " + ); + + return jdbcDao.queryForInts(sql.toString(), req); + } + + public List getUserGroupNames(Map req){ + StringBuilder sql = new StringBuilder("SELECT" + + " g.name " + + " FROM user_group ug " + + " LEFT JOIN `group` g ON g.id = ug.groupId " + + " WHERE userId = :id; " + ); + return jdbcDao.queryForStrings(sql.toString(), req); + } + + + // @Transactional(rollbackFor = Exception.class) + public List search(SearchUserReq req) { + StringBuilder sql = new StringBuilder("SELECT" + + " DISTINCT u.id," + + " u.created," + + " u.createdBy," + + " u.version," + + " u.modified," + + " u.modifiedBy," + + " u.username," + + " u.locked," + + " u.name," + + " u.locale," + + " u.firstname," + + " u.lastname," + + " u.title," + + " u.department," + + " u.email," + + " u.phone1," + + " u.phone2," + + " u.remarks, " + + " u.fullname, " + + " u.post " + + " FROM `user` u" + + " left join user_group ug on u.id = ug.userId" + + " where u.deleted = false"); + + if (req != null) { + if (req.getId() != null) + sql.append(" AND u.id = :id"); + + if (req.getGroupId() != null) + sql.append(" AND ug.groupId = :groupId"); + if (StringUtils.isNotBlank(req.getUsername())) { + req.setUsername("%" + req.getUsername() + "%"); + sql.append(" AND u.username LIKE :username"); + } + + if (StringUtils.isNotBlank(req.getFullname())) { + req.setFullname("%" + req.getFullname() + "%"); + sql.append(" AND u.fullname LIKE :fullname"); + } + + if (StringUtils.isNotBlank(req.getPost())) { + req.setPost("%" + req.getPost() + "%"); + sql.append(" AND u.post LIKE :post"); + } + + if (StringUtils.isNotBlank(req.getEmail())) { + req.setEmail("%" + req.getEmail() + "%"); + sql.append(" AND u.email LIKE :email"); + } + + if (StringUtils.isNotBlank(req.getPhone())) { + req.setPhone("%" + req.getPhone() + "%"); + sql.append(" AND u.phone1 LIKE :phone"); + } + + if (req.getLocked() != null) { + sql.append(" AND u.locked = :locked"); + } + } + sql.append(" ORDER BY u.username asc"); + + if (req != null) { + if (req.getStart() != null && req.getLimit() != null) + sql.append(" LIMIT :start, :limit"); + } + + return jdbcDao.queryForList(sql.toString(), req, UserRecord.class); + } + + public List listUserAuthId(long id) { + return jdbcDao.queryForInts( + "SELECT" + + " ua.authId" + + " FROM user_authority ua" + + " WHERE ua.userId = :id", + Map.of(Params.ID, id)); + } + + public List listUserGroupId(long id) { + return jdbcDao.queryForInts( + "SELECT" + + " gu.groupId" + + " FROM user_group gu" + + " INNER JOIN `group` g ON g.deleted = FALSE AND g.id = gu.groupId" + + " WHERE gu.userId = :id", + Map.of(Params.ID, id)); + } + + private User saveOrUpdate(User instance, UpdateUserReq req) { + + if (instance.getId() == null) { + req.setLocked(false); + } + BeanUtils.copyProperties(req, instance); + instance = save(instance); + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public void updateRecord(Integer id, UpdateUserReq req) { + saveOrUpdate( + find(id.longValue()).orElseThrow(NotFoundException::new), + req); + } + + private User saveOrUpdateArs(User instance, NewFhsmsCUserReq req) { + + if (instance.getId() == null) { + req.setLocked(false); + } + BeanUtils.copyProperties(req, instance); + instance = saveAndFlush(instance); + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public User newRecord(NewUserReq req) throws UnsupportedEncodingException { + if (findByUsername(req.getUsername()).isPresent()) { + throw new UnprocessableEntityException(ErrorCodes.USERNAME_NOT_AVAILABLE); + } + User instance = new User(); + // instance.setPassword(pwdHash); + instance = saveOrUpdate(instance, req); + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public User newPublicUserRecord(NewPublicUserReq req) throws UnsupportedEncodingException { + if (findByUsername(req.getUsername()).isPresent()) { + throw new UnprocessableEntityException(ErrorCodes.USERNAME_NOT_AVAILABLE); + } + + String submitedPassword = req.getPassword(); + String pwdHash = passwordEncoder.encode(submitedPassword); + req.setPassword(pwdHash); + User instance = new User(); + + instance = saveOrUpdate(instance, req); + return instance; + } + + @Transactional(rollbackFor = Exception.class) + public User newUserRecord(NewFhsmsCUserReq req) + throws UnsupportedEncodingException, NoSuchMessageException, ParseException, IllegalAccessException { + User instance = new User(); + + if (req.getId() > 0) { + instance = find(req.getId()).get(); + } else { + if (findByUsername(req.getUsername()).isPresent()) { + throw new UnprocessableEntityException(ErrorCodes.USERNAME_NOT_AVAILABLE); + } + instance = new User(); + } + + if (req.getPassword() != null && req.getPassword() != "" && req.getPassword().length() > 1) { + String submitedPassword = req.getPassword(); + + PasswordRule rule = new PasswordRule(settingsService); + if (!PasswordUtils.checkPwd(req.getPassword(), rule, req.getUsername())) { + throw new UnprocessableEntityException(rule.getWrongMsg()); + } + if (req.getId() > 0) { + List passHists = userPasswordHistoryService.getPasswordHistory(req.getId()); + passHists.stream().forEach(oldPass -> { + if (passwordEncoder.matches(req.getPassword(), oldPass)) { + throw new UnprocessableEntityException("This password is already in the password history."); + } + }); + } + + String pwdHash = passwordEncoder.encode(submitedPassword); + req.setPassword(pwdHash); + + } else { + req.setPassword(instance.getPassword()); + } + + // =====GET OLD AUDIT LOG=====// + String tableName = instance.getClass().getAnnotation(Table.class).name(); + Map oldUserObject = new HashMap(); + Map newUserObject = new HashMap(); + + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData = getAuditLogObject(input); + logData.put("user_auth", getUserAuthNames(input)); + logData.put("user_auth_id", getUserAuthIds(input)); + logData.put("user_group", getUserGroupNames(input)); + logData.put("user_group_id", getUserGroupIds(input)); + oldUserObject = logData; + } + // =====GET OLD AUDIT LOG=====// + + instance = saveOrUpdateArs(instance, req); + Long id = instance.getId(); + + if (req.getPassword() != null && req.getPassword() != "" && req.getPassword().length() > 1) { + // if password updated + UserPasswordHistory record = new UserPasswordHistory(); + record.setUserId(id); + record.setPassword(req.getPassword()); + record.setDate(LocalDateTime.now()); + record = userPasswordHistoryService.save(record); + } + + List> authBatchInsertValues = req.getAddAuthIds().stream() + .map(authId -> Map.of("userId", id, "authId", authId)) + .collect(Collectors.toList()); + List> authBatchDeleteValues = req.getRemoveAuthIds().stream() + .map(authId -> Map.of("userId", id, "authId", authId)) + .collect(Collectors.toList()); + + if (!authBatchInsertValues.isEmpty()) { + jdbcDao.batchUpdate( + "INSERT IGNORE INTO user_authority (userId, authId)" + + " VALUES (:userId, :authId)", + authBatchInsertValues); + } + if (!authBatchDeleteValues.isEmpty()) { + jdbcDao.batchUpdate( + "DELETE FROM user_authority" + + " WHERE userId = :userId AND authId = :authId", + authBatchDeleteValues); + } + + List> userBatchInsertValues = req.getAddGroupIds().stream() + .map(groupId -> Map.of("userId", id, "groupId", groupId)) + .collect(Collectors.toList()); + List> userBatchDeleteValues = req.getRemoveGroupIds().stream() + .map(groupId -> Map.of("userId", id, "groupId", groupId)) + .collect(Collectors.toList()); + + if (!userBatchInsertValues.isEmpty()) { + jdbcDao.batchUpdate( + "INSERT IGNORE INTO user_group (groupId,userId)" + + " VALUES (:groupId, :userId)", + userBatchInsertValues); + } + if (!userBatchDeleteValues.isEmpty()) { + jdbcDao.batchUpdate( + "DELETE FROM user_group" + + " WHERE groupId = :groupId AND userId = :userId", + userBatchDeleteValues); + } + + // =====GET NEW AUDIT LOG=====// + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData = getAuditLogObject(input); + logData.put("user_auth", getUserAuthNames(input)); + logData.put("user_auth_id", getUserAuthIds(input)); + logData.put("user_group", getUserGroupNames(input)); + logData.put("user_group_id", getUserGroupIds(input)); + newUserObject = logData; + } + + + if(auditLogService.compareMaps(newUserObject,oldUserObject).size() != 0 || + auditLogService.compareMaps(oldUserObject,newUserObject).size() != 0 + ){ + auditLogService.save( + tableName, + instance.getId(), + instance.getName(), + SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null, + new Date(), + JsonUtils.toJsonString(auditLogService.compareMaps(newUserObject,oldUserObject)), + JsonUtils.toJsonString(auditLogService.compareMaps(oldUserObject,newUserObject))); + } + // =====GET NEW AUDIT LOG=====// + return instance; + } + + + @Transactional(rollbackFor = Exception.class) + public void updateRecord(long id, UpdateUserReq req) { + saveOrUpdate( + find(id).orElseThrow(NotFoundException::new), + req); + } + + @Transactional(rollbackFor = Exception.class) + public String resetPassword(long id) throws UnsupportedEncodingException { + User instance = find(id).orElseThrow(NotFoundException::new); + instance = save(instance); + return null; + } + + @Transactional(rollbackFor = Exception.class) + public List> listUserAuth(Map args) { + + StringBuilder sql = new StringBuilder("SELECT" + + " a.id, " + + " a.module," + + " a.authority," + + " a.name," + + " a.description, "); + if (args.containsKey("userId")) + sql.append(" EXISTS(SELECT 1 FROM user_authority WHERE authority = a.authority AND userId = :userId) AS v"); + else + sql.append(" 0 AS v"); + sql.append(" FROM authority a" + + " ORDER BY a.module, a.name"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public List> searchForCombo(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " u.id, " + + " u.id as `key`, " + + " u.name, " + + " u.name as label " + + " FROM `user` u " + + " WHERE u.deleted = FALSE" + ); + + if (args != null) { + if (args.containsKey(Params.QUERY)) + sql.append(" AND (u.name LIKE :query)"); + if (args.containsKey(Params.ID)) + sql.append(" AND u.id = :id"); + } + + sql.append(" ORDER BY u.name"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public List> searchForAuditCombo(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " u.id, " + + " u.id as `key`, " + + " u.name, " + + " u.name as label " + + " FROM `user` u " + ); + + sql.append(" ORDER BY u.name"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public List> listLoginLog(Map args) { + StringBuilder sql = new StringBuilder("SELECT" + + " ull.username, " + + " ull.ipAddr, " + + " ull.loginTime, " + + " ull.success " + + " FROM user_login_log ull " + + " WHERE TRUE "); + + if (args != null) { + if (args.containsKey("username")) + sql.append(" AND (ull.username LIKE :username)"); + if (args.containsKey("ipAddr")) + sql.append(" AND ull.ipAddr LIKE :ipAddr"); + + if (args.containsKey("fromDate")) + sql.append(" AND DATE(ull.loginTime) >= :fromDate"); + if (args.containsKey("toDate")) + sql.append(" AND DATE(ull.loginTime) < :toDate"); + + if (args.containsKey("success")) + sql.append(" AND ull.success = :success"); + } + + sql.append(" ORDER BY ull.loginTime DESC"); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public Map softDelete(User user) { + User instance = user; + + // =====GET OLD AUDIT LOG=====// + String tableName = instance.getClass().getAnnotation(Table.class).name(); + Map oldUserObject = new HashMap(); + Map newUserObject = new HashMap(); + + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData = getAuditLogObject(input); + logData.put("user_auth", getUserAuthNames(input)); + logData.put("user_auth_id", getUserAuthIds(input)); + logData.put("user_group", getUserGroupNames(input)); + logData.put("user_group_id", getUserGroupIds(input)); + oldUserObject = logData; + } + // =====GET OLD AUDIT LOG=====// + this.markDelete(instance); + + // =====GET NEW AUDIT LOG=====// + if (instance != null && instance.getId() != null && instance.getId() > 0) { + Map input = Map.of("id", instance.getId()); + Map logData = getAuditLogObject(input); + logData.put("user_auth", getUserAuthNames(input)); + logData.put("user_auth_id", getUserAuthIds(input)); + logData.put("user_group", getUserGroupNames(input)); + logData.put("user_group_id", getUserGroupIds(input)); + newUserObject = logData; + } + + if(auditLogService.compareMaps(newUserObject,oldUserObject).size() != 0 || + auditLogService.compareMaps(oldUserObject,newUserObject).size() != 0 + ){ + auditLogService.save( + tableName, + instance.getId(), + instance.getName(), + SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null, + new Date(), + JsonUtils.toJsonString(auditLogService.compareMaps(newUserObject,oldUserObject)), + JsonUtils.toJsonString(auditLogService.compareMaps(oldUserObject,newUserObject))); + } + // =====GET NEW AUDIT LOG=====// + + return Map.of( + "id", instance.getId()); + } + + public boolean isNameTaken(String name, Long id) { + StringBuilder sql = new StringBuilder("SELECT" + + " Count(u.id) " + + " FROM `user` u " + + " WHERE u.deleted = FALSE " + + " AND u.name = :name " + + " AND u.id != :id "); + + return jdbcDao.queryForBoolean(sql.toString(), Map.of("name", name, "id", id)); + } + + public Integer getLastChangePasswordDate(Long id) { + StringBuilder sql = new StringBuilder("SELECT" + + " TIMESTAMPDIFF(DAY,uph.`date`, NOW()) " + + " FROM user_password_history uph " + + " WHERE uph.userId = :userId " + + " ORDER BY uph.id DESC " + + " LIMIT 1 " + ); + + return jdbcDao.queryForInt(sql.toString(), Map.of("userId", id)); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/AuthRecord.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/AuthRecord.java new file mode 100644 index 0000000..7f4be43 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/AuthRecord.java @@ -0,0 +1,41 @@ +package com.ffii.fhsmsc.modules.user.service.pojo; + +public class AuthRecord { + private Integer id; + private String module; + private String authority; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getAuthority() { + return authority; + } + + public void setAuthority(String authority) { + this.authority = authority; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/UserRecord.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/UserRecord.java new file mode 100644 index 0000000..fa2d54c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/pojo/UserRecord.java @@ -0,0 +1,167 @@ +package com.ffii.fhsmsc.modules.user.service.pojo; + +import java.time.LocalDateTime; + +public class UserRecord { + private Integer id; + private LocalDateTime created; + private String createdBy; + private String modified; + private String modifiedBy; + private String username; + private Boolean locked; + private String name; + private Integer companyId; + private Integer customerId; + private String locale; + private String fullname; + private String firstname; + private String lastname; + private String title; + private String department; + private String deptId; + private String email; + private String phone1; + private String phone2; + private String remarks; + private String post; + + public Boolean isLocked() { + return this.locked; + } + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getPost() { + return this.post; + } + + public void setPost(String post) { + this.post = post; + } + + public Integer getId() { + return id; + } + public void setId(Integer id) { + this.id = id; + } + public LocalDateTime getCreated() { + return created; + } + public void setCreated(LocalDateTime created) { + this.created = created; + } + public String getCreatedBy() { + return createdBy; + } + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + public String getModified() { + return modified; + } + public void setModified(String modified) { + this.modified = modified; + } + public String getModifiedBy() { + return modifiedBy; + } + public void setModifiedBy(String modifiedBy) { + this.modifiedBy = modifiedBy; + } + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + public Boolean getLocked() { + return locked; + } + public void setLocked(Boolean locked) { + this.locked = locked; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public Integer getCompanyId() { + return companyId; + } + public void setCompanyId(Integer companyId) { + this.companyId = companyId; + } + public Integer getCustomerId() { + return customerId; + } + public void setCustomerId(Integer customerId) { + this.customerId = customerId; + } + public String getLocale() { + return locale; + } + public void setLocale(String locale) { + this.locale = locale; + } + public String getFullname() { + return fullname; + } + public void setFullname(String fullname) { + this.fullname = fullname; + } + public String getFirstname() { + return firstname; + } + public void setFirstname(String firstname) { + this.firstname = firstname; + } + public String getLastname() { + return lastname; + } + public void setLastname(String lastname) { + this.lastname = lastname; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + public String getDepartment() { + return department; + } + public void setDepartment(String department) { + this.department = department; + } + public String getDeptId() { + return deptId; + } + public void setDeptId(String deptId) { + this.deptId = deptId; + } + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + public String getPhone1() { + return phone1; + } + public void setPhone1(String phone1) { + this.phone1 = phone1; + } + public String getPhone2() { + return phone2; + } + public void setPhone2(String phone2) { + this.phone2 = phone2; + } + public String getRemarks() { + return remarks; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/service/res/LoadUserRes.java b/src/main/java/com/ffii/fhsmsc/modules/user/service/res/LoadUserRes.java new file mode 100644 index 0000000..3f85573 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/service/res/LoadUserRes.java @@ -0,0 +1,45 @@ +package com.ffii.fhsmsc.modules.user.service.res; + +import java.util.List; + +import com.ffii.fhsmsc.modules.user.entity.User; + +public class LoadUserRes { + private User data; + private List authIds; + private List groupIds; + + public LoadUserRes() { + } + + public LoadUserRes(User data, List authIds, List groupIds) { + this.data = data; + this.authIds = authIds; + this.groupIds = groupIds; + } + + public User getData() { + return data; + } + + public void setData(User data) { + this.data = data; + } + + public List getAuthIds() { + return authIds; + } + + public void setAuthIds(List authIds) { + this.authIds = authIds; + } + + public List getGroupIds() { + return groupIds; + } + + public void setGroupIds(List groupIds) { + this.groupIds = groupIds; + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/web/AuditLogController.java b/src/main/java/com/ffii/fhsmsc/modules/user/web/AuditLogController.java new file mode 100644 index 0000000..a27ff10 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/web/AuditLogController.java @@ -0,0 +1,38 @@ +package com.ffii.fhsmsc.modules.user.web; + +import java.util.Map; + +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.common.service.AuditLogService; + +import jakarta.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping("/auditLog") +public class AuditLogController{ + + private AuditLogService auditLogService; + + public AuditLogController(AuditLogService auditLogService) { + this.auditLogService = auditLogService; + } + + @GetMapping + public RecordsRes> list(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(auditLogService.arsSearch( + CriteriaArgsBuilder.withRequest(request) + .addInteger("modifiedBy") + .addString("tableName") + .addStringLike("recordName") + .addDate("fromDate") + .addDateTo("toDate") + .build())); + } + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/web/GroupController.java b/src/main/java/com/ffii/fhsmsc/modules/user/web/GroupController.java new file mode 100644 index 0000000..37dfdee --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/web/GroupController.java @@ -0,0 +1,103 @@ +package com.ffii.fhsmsc.modules.user.web; + +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.core.utils.Params; +import com.ffii.fhsmsc.modules.user.req.SaveGroupReq; +import com.ffii.fhsmsc.modules.user.service.GroupService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/group") +public class GroupController{ + + private GroupService groupService; + + public GroupController( + GroupService groupService + ) { + this.groupService = groupService; + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid SaveGroupReq req) { + return new IdRes(groupService.saveOrUpdate(req).getId()); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long id) { + groupService.delete(groupService.find(id).orElseThrow(NotFoundException::new)); + } + + @GetMapping("/{id}") + public Map load(@PathVariable Long id) { + return Map.of( + Params.DATA, groupService.find(id).orElseThrow(NotFoundException::new), + "authIds", groupService.listGroupAuthId(id), + "userIds", groupService.listGroupUserId(id)); + } + + @GetMapping("/combo") + public RecordsRes> comboJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(groupService.searchForCombo( + CriteriaArgsBuilder.withRequest(request) + .addInteger(Params.ID) + .addStringLike(Params.QUERY) + .build())); + } + + @GetMapping + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(groupService.search( + CriteriaArgsBuilder.withRequest(request) + .addInteger(Params.ID) + .addStringLike(Params.NAME) + .addStringLike("description") + .addInteger("userId") + .build())); + } + + @GetMapping("/member") + public RecordsRes> listGroupUser(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(groupService.listGroupMember( + CriteriaArgsBuilder.withRequest(request) + .addInteger(Params.ID) + .build())); + } + + @GetMapping("/auth/combo") + public RecordsRes> authComboJson(HttpServletRequest request) throws ServletRequestBindingException { + System.out.println(request); + return new RecordsRes<>(groupService.listGroupAuth( + CriteriaArgsBuilder.withRequest(request) + .addInteger("groupId") + .build())); + } + + @GetMapping("/checkDuplicate") + public Map checkDuplicate(@RequestParam String name, @RequestParam Long id) { + boolean isNameTaken = groupService.isNameTaken(name, id); + return Map.of( + "isTaken", isNameTaken + ); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user/web/UserController.java b/src/main/java/com/ffii/fhsmsc/modules/user/web/UserController.java new file mode 100644 index 0000000..1d14bbf --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user/web/UserController.java @@ -0,0 +1,299 @@ +package com.ffii.fhsmsc.modules.user.web; + +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.NoSuchMessageException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.exception.UnprocessableEntityException; +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.core.utils.Params; +import com.ffii.core.utils.PasswordUtils; +import com.ffii.fhsmsc.modules.common.PasswordRule; +import com.ffii.fhsmsc.modules.common.SecurityUtils; +import com.ffii.fhsmsc.modules.common.SettingNames; +import com.ffii.fhsmsc.modules.settings.service.SettingsService; +import com.ffii.fhsmsc.modules.user.entity.User; +import com.ffii.fhsmsc.modules.user.entity.UserPasswordHistory; +import com.ffii.fhsmsc.modules.user.req.NewFhsmsCUserReq; +import com.ffii.fhsmsc.modules.user.req.NewPublicUserReq; +import com.ffii.fhsmsc.modules.user.req.NewUserReq; +import com.ffii.fhsmsc.modules.user.req.SearchUserReq; +import com.ffii.fhsmsc.modules.user.req.UpdateUserReq; +import com.ffii.fhsmsc.modules.user.service.UserPasswordHistoryService; +import com.ffii.fhsmsc.modules.user.service.UserService; +import com.ffii.fhsmsc.modules.user.service.res.LoadUserRes; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; + +@RestController +@RequestMapping("/user") +@Tag(name = "Tutorial", description = "Tutorial management APIs") +public class UserController { + + private final Log logger = LogFactory.getLog(getClass()); + private UserService userService; + private PasswordEncoder passwordEncoder; + private SettingsService settingsService; + private UserPasswordHistoryService userPasswordHistoryService; + + private Integer passwordHist; + public UserController( + UserService userService, + PasswordEncoder passwordEncoder, + SettingsService settingsService, + UserPasswordHistoryService userPasswordHistoryService) { + this.userService = userService; + this.passwordEncoder = passwordEncoder; + this.settingsService = settingsService; + this.userPasswordHistoryService = userPasswordHistoryService; + this.passwordHist = settingsService.getInt(SettingNames.SYS_PASSWORD_RULE_HISTORY); + + } + + @Operation(summary = "list user", security = @SecurityRequirement(name = "bearerAuth"), responses = { @ApiResponse(responseCode = + "200"), + @ApiResponse(responseCode = "404", content = @Content) }) + @GetMapping + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public ResponseEntity list(@ModelAttribute @Valid SearchUserReq req) { + return ResponseEntity.ok(userService.search(req)); + } + + @Operation(summary = "load user data", security = @SecurityRequirement(name = "bearerAuth"), responses = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "404", content = @Content) }) + @GetMapping("/{id}") + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public LoadUserRes load(@PathVariable long id) { + LoadUserRes test = new LoadUserRes( + userService.find(id).orElseThrow(NotFoundException::new), + userService.listUserAuthId(id), + userService.listUserGroupId(id)); + return test; + } + + @Operation(summary = "delete user", responses = { @ApiResponse(responseCode = + "204"), + @ApiResponse(responseCode = "404", content = @Content) }) + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public void delete(@PathVariable long id) { + userService.softDelete(userService.find(id).orElseThrow(NotFoundException::new)); + } + + @Operation(summary = "new user") + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public IdRes newRecord(@RequestBody @Valid NewUserReq req) throws UnsupportedEncodingException { + return new IdRes(userService.newRecord(req).getId()); + } + + @Operation(summary = "new user by public user") + @PostMapping("/registry") + @ResponseStatus(HttpStatus.CREATED) + // @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public ResponseEntity createPublicUserRecord(@RequestBody NewPublicUserReq req) + throws UnsupportedEncodingException { + logger.info("Create user request:"); + return ResponseEntity.ok(new IdRes(userService.newPublicUserRecord(req).getId())); + } + + @PostMapping("/admin/registry") + @ResponseStatus(HttpStatus.CREATED) + // @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public ResponseEntity createUserRecord(@RequestBody NewFhsmsCUserReq req) + throws UnsupportedEncodingException, NoSuchMessageException, ParseException, IllegalAccessException { + return ResponseEntity.ok(new IdRes(userService.newUserRecord(req).getId())); + } + + @Operation(summary = "update user", responses = { + @ApiResponse(responseCode = "204"), + @ApiResponse(responseCode = "400", content = @Content), + @ApiResponse(responseCode = "404", content = @Content), + }) + + @PutMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public void updateRecord(@PathVariable int id, @RequestBody @Valid UpdateUserReq req) { + userService.updateRecord(id, req); + } + + // @Operation(summary = "current user change password", description = "error: + // USER_WRONG_NEW_PWD = new password not available", responses = { + // @ApiResponse(responseCode = "204"), + // @ApiResponse(responseCode = "400", content = @Content), + // @ApiResponse(responseCode = "404", content = @Content), + // @ApiResponse(responseCode = "422", content = @Content(schema = + // @Schema(implementation = FailureRes.class))), + // }) + @PatchMapping("/change-password") + @ResponseStatus(HttpStatus.NO_CONTENT) + // @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public void changePassword(@RequestBody @Valid ChangePwdReq req) { + long id = SecurityUtils.getUser().get().getId(); + User instance = userService.find(id).orElseThrow(NotFoundException::new); + + logger.info("TEST req: " + req.getPassword()); + logger.info("TEST instance: " + instance.getPassword()); + + if (!passwordEncoder.matches(req.getPassword(), instance.getPassword())) { + throw new UnprocessableEntityException("Incorrect existing password"); + } + + PasswordRule rule = new PasswordRule(settingsService); + if (!PasswordUtils.checkPwd(req.getNewPassword(), rule, instance.getUsername())) { + throw new UnprocessableEntityException(rule.getWrongMsg()); + } + + List passHists = userPasswordHistoryService.getPasswordHistory(id); + passHists.stream().forEach(oldPass -> { + if (passwordEncoder.matches(req.getNewPassword(), oldPass)) { + throw new UnprocessableEntityException("The password already exists in the history of the past " + passwordHist + " times."); + } + }); + + instance.setPassword(passwordEncoder.encode(req.getNewPassword())); + userService.save(instance); + + // if password updated + UserPasswordHistory record = new UserPasswordHistory(); + record.setUserId(id); + record.setPassword(instance.getPassword()); + record.setDate(LocalDateTime.now()); + record = userPasswordHistoryService.save(record); + } + + // @Operation(summary = "reset password", responses = { + // @ApiResponse(responseCode = "204"), + // @ApiResponse(responseCode = "404", content = @Content), + // }) + @PostMapping("/{id}/reset-password") + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity resetPassword(@PathVariable long id) throws UnsupportedEncodingException { + String password = userService.resetPassword(id); + return ResponseEntity.ok(password); + } + + // @Operation(summary = "get password rules") + @GetMapping("/password-rule") + public PasswordRule passwordRlue() { + return new PasswordRule(settingsService); + } + + public static class ChangePwdReq { + @NotBlank + private String password; + @NotBlank + private String newPassword; + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } + + } + + @GetMapping("/auth/combo") + public RecordsRes> authComboJson(HttpServletRequest request) + throws ServletRequestBindingException { + System.out.println(request); + return new RecordsRes<>(userService.listUserAuth( + CriteriaArgsBuilder.withRequest(request) + .addInteger("userId") + .build())); + } + + @GetMapping("/combo") + public RecordsRes> comboJson(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userService.searchForCombo( + CriteriaArgsBuilder.withRequest(request) + .addInteger(Params.ID) + .addStringLike(Params.QUERY) + .build())); + } + + @GetMapping("/audit/combo") + public RecordsRes> comboJsonWithDeletedUser(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(userService.searchForAuditCombo( + CriteriaArgsBuilder.withRequest(request) + .addInteger(Params.ID) + .addStringLike(Params.QUERY) + .build())); + } + + @GetMapping("/loginLog") + @PreAuthorize("hasAuthority('MAINTAIN_USER')") + public RecordsRes> list(HttpServletRequest request) throws ServletRequestBindingException { + System.out.println(request); + List> temp = userService.listLoginLog( + CriteriaArgsBuilder.withRequest(request) + .addStringLike("username") + .addStringLike("ipAddr") + .addDate("fromDate") + .addDateTo("toDate") + .build()); + return new RecordsRes<>(temp); + } + + @GetMapping("/checkDuplicate") + public Map checkDuplicate(@RequestParam String name, Long id) { + boolean isNameTaken = userService.isNameTaken(name, id); + return Map.of( + "isTaken", isNameTaken); + } + + @GetMapping("/isPasswordExpired") + public Map getIsPasswordExpired(@RequestParam Long id) { + Integer maximumDay = settingsService.getInt(SettingNames.SYS_PASSWORD_RULE_DURATION); + Integer dayDiff = userService.getLastChangePasswordDate(id); + return Map.of("expired", dayDiff >= maximumDay); + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/User_information.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/User_information.java new file mode 100644 index 0000000..aafa579 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/User_information.java @@ -0,0 +1,132 @@ +package com.ffii.fhsmsc.modules.user_information.entity; + +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** @author Terence */ +@Entity +@Table(name = "user_information") +public class User_information extends BaseEntity { + @Column + private Long user_id; + @Column + private Long id; + //@NotBlank + + + @Column + private String gender; + + @Column + private String name; + + @Column + private LocalDateTime date_of_birth; + + @Column + private String height; + + @Column + private String weight; + + + + @Column + private String goal; + + + @Column + private String experience; + + @Column + private String activitylevel; + + + // getter & setter + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + public Long getuser_id() { + return user_id; + } + + public void setuser_id(Long user_id) { + this.user_id = user_id; + } + + public String getgender() { + return gender; + } + + public void setgender(String gender) { + this.gender = gender; + } + + public String getname() { + return name; + } + + public void setname(String name) { + this.name = name; + } + + public LocalDateTime getdate_of_birth() { + return date_of_birth; + } + + public void setdate_of_birth(LocalDateTime date_of_birth) { + this.date_of_birth = date_of_birth; + } + + public String getheight() { + return height; + } + + public void setheight(String height) { + this.height = height; + } + + public String getweight() { + return weight; + } + + public void setweight(String weight) { + this.weight = weight; + } + + + public String getgoal() { + return goal; + } + + public void setgoal(String goal) { + this.goal = goal; + } + + public String getexperience() { + return experience; + } + + public void setexperience(String experience) { + this.experience = experience; + } + public String getactivitylevel() { + return activitylevel; + } + + public void setactivitylevel(String activitylevel) { + this.activitylevel = activitylevel; + } + + +} + diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/user_informationRepository.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/user_informationRepository.java new file mode 100644 index 0000000..e9f855c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/entity/user_informationRepository.java @@ -0,0 +1,7 @@ +package com.ffii.fhsmsc.modules.user_information.entity; + +import com.ffii.core.support.AbstractRepository; + +public interface user_informationRepository extends AbstractRepository { + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/req/Saveuser_informationReq.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/req/Saveuser_informationReq.java new file mode 100644 index 0000000..cd9a8f4 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/req/Saveuser_informationReq.java @@ -0,0 +1,101 @@ +package com.ffii.fhsmsc.modules.user_information.req; + +import java.time.LocalDateTime; + +import jakarta.validation.constraints.NotNull; + +public class Saveuser_informationReq { + private Long id; + @NotNull + private Long user_id; + private String gender; + private String name; + private LocalDateTime date_of_birth; + private String height; + private String weight; + private String goal; + private String experience; + private String activitylevel; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getuser_id() { + return user_id; + } + + public void setuser_id(Long user_id) { + this.user_id = user_id; + } + + public String getgender() { + return gender; + } + + public void setgender(String gender) { + this.gender = gender; + } + + public String getname() { + return name; + } + + public void setname(String name) { + this.name = name; + } + + public LocalDateTime getdate_of_birth() { + return date_of_birth; + } + + public void setdate_of_birth(LocalDateTime date_of_birth) { + this.date_of_birth = date_of_birth; + } + + public String getheight() { + return height; + } + + public void setheight(String height) { + this.height = height; + } + + public String getweight() { + return weight; + } + + public void setweight(String weight) { + this.weight = weight; + } + + + public String getgoal() { + return goal; + } + + public void setgoal(String goal) { + this.goal = goal; + } + + public String getexperience() { + return experience; + } + + public void setexperience(String experience) { + this.experience = experience; + } + public String getactivitylevel() { + return activitylevel; + } + + public void setactivitylevel(String activitylevel) { + this.activitylevel = activitylevel; + } + + +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/req/UserInfoCombineReq.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/req/UserInfoCombineReq.java new file mode 100644 index 0000000..935c59c --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/req/UserInfoCombineReq.java @@ -0,0 +1,30 @@ +package com.ffii.fhsmsc.modules.user_information.req; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +public class UserInfoCombineReq { + @Valid + @NotNull + private Saveuser_informationReq userInformation; + + private List allergyIds; + + // Getters and setters + public Saveuser_informationReq getUserInformation() { + return userInformation; + } + + public void setUserInformation(Saveuser_informationReq userInformation) { + this.userInformation = userInformation; + } + + public List getAllergyIds() { + return allergyIds; + } + + public void setAllergyIds(List allergyIds) { + this.allergyIds = allergyIds; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/service/UserInformationCombineService.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/service/UserInformationCombineService.java new file mode 100644 index 0000000..1ee5479 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/service/UserInformationCombineService.java @@ -0,0 +1,56 @@ +package com.ffii.fhsmsc.modules.user_information.service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.fhsmsc.modules.allergy.service.UserAllergyService; +import com.ffii.fhsmsc.modules.user_information.entity.User_information; +import com.ffii.fhsmsc.modules.user_information.req.UserInfoCombineReq; + +@Service +public class UserInformationCombineService { + private final user_informationService userInformationService; + private final UserAllergyService userAllergyService; + + public UserInformationCombineService( + user_informationService userInformationService, + UserAllergyService userAllergyService) { + this.userInformationService = userInformationService; + this.userAllergyService = userAllergyService; + } + + public Map getCombinedUserInfo(Integer userId) { + Map args = new HashMap<>(); + args.put("user_id", userId); + + // 获取用户基本信息 + List> userInfo = userInformationService.search(args); + // 获取用户过敏信息 + List> userAllergyInfo = userAllergyService.search(args); + + Map combinedInfo = new HashMap<>(); + combinedInfo.put("user_information", userInfo.isEmpty() ? null : userInfo.get(0)); + combinedInfo.put("allergies", userAllergyInfo); + + return combinedInfo; + } + + @Transactional(rollbackFor = Exception.class) + public Long saveCombinedInfo(UserInfoCombineReq req) { + // 保存用户基本信息 + User_information userInfo = userInformationService.saveOrUpdate(req.getUserInformation()); + + // 更新用户过敏信息 + if (req.getAllergyIds() != null) { + // 将 Long 转换为 int + int userId = req.getUserInformation().getuser_id().intValue(); + userAllergyService.updateUserAllergies(userId, req.getAllergyIds()); + } + + return userInfo.getId(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/service/user_informationService.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/service/user_informationService.java new file mode 100644 index 0000000..d00c453 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/service/user_informationService.java @@ -0,0 +1,106 @@ +package com.ffii.fhsmsc.modules.user_information.service; + +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.ffii.core.exception.InternalServerErrorException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.fhsmsc.modules.user_information.entity.User_information; +import com.ffii.fhsmsc.modules.user_information.entity.user_informationRepository; +import com.ffii.fhsmsc.modules.user_information.req.Saveuser_informationReq; + +import jakarta.validation.Valid; + +@Service +public class user_informationService extends AbstractBaseEntityService{ + private static final Logger logger = LoggerFactory.getLogger(user_informationService.class); + + public user_informationService(JdbcDao jdbcDao, user_informationRepository repository) { + super(jdbcDao, repository); + } + + public List> search(Map args) { + logger.info("Search called with args: {}", args); + StringBuilder sql = new StringBuilder("SELECT" + + " fi.id," + + " fi.user_id," + + " fi.gender," + + " fi.name," + + " fi.activitylevel," + + " fi.date_of_birth," + + " fi.height," + + " fi.weight," + + " fi.goal," + + " fi.experience" + + " FROM user_information fi" + + " WHERE 1=1" + ); + if (args != null) { + if (args.containsKey("user_id")) { + sql.append(" AND fi.user_id = :user_id"); + logger.info("Added user_id filter: {}", args.get("user_id")); + } + } + + sql.append(" ORDER BY fi.id"); + logger.info("Final SQL query: {}", sql.toString()); + + List> result = jdbcDao.queryForList(sql.toString(), args); + logger.info("Query returned {} results", result.size()); + return result; + } + + @Transactional(rollbackFor = Exception.class) + public User_information saveOrUpdate(@Valid Saveuser_informationReq req) { + User_information instance; + + if (req.getId() != null && req.getId() > 0) { + // 更新现有记录 + instance = find(req.getId()).orElseThrow(InternalServerErrorException::new); + + // 只更新非空字段 + if (req.getuser_id() != null) { + instance.setuser_id(req.getuser_id()); + } + if (req.getgender() != null) { + instance.setgender(req.getgender()); + } + if (req.getname() != null) { + instance.setname(req.getname()); + } + if (req.getdate_of_birth() != null) { + instance.setdate_of_birth(req.getdate_of_birth()); + } + if (req.getheight() != null) { + instance.setheight(req.getheight()); + } + if (req.getweight() != null) { + instance.setweight(req.getweight()); + } + + if (req.getgoal() != null) { + instance.setgoal(req.getgoal()); + } + if (req.getexperience() != null) { + instance.setexperience(req.getexperience()); + } + if (req.getactivitylevel() != null) { + instance.setactivitylevel(req.getactivitylevel()); + } + } else { + // 创建新记录 + instance = new User_information(); + BeanUtils.copyProperties(req, instance); + } + + saveAndFlush(instance); + return instance; + } +} diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/web/UserInformationCombineController.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/web/UserInformationCombineController.java new file mode 100644 index 0000000..f41f320 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/web/UserInformationCombineController.java @@ -0,0 +1,32 @@ +package com.ffii.fhsmsc.modules.user_information.web; + +import org.springframework.web.bind.annotation.*; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.response.IdRes; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; // 添加这个导入 +import java.util.Map; +import java.util.List; // 添加这个导入 +import com.ffii.fhsmsc.modules.user_information.service.UserInformationCombineService; +import com.ffii.fhsmsc.modules.user_information.req.UserInfoCombineReq; + +@RestController +@RequestMapping("/user_information_combine") +public class UserInformationCombineController { + private final UserInformationCombineService userInformationCombineService; + + public UserInformationCombineController(UserInformationCombineService userInformationCombineService) { + this.userInformationCombineService = userInformationCombineService; + } + + @GetMapping("/get") + public RecordsRes> getUserCombinedInfo(@RequestParam("user_id") Integer userId) { + Map result = userInformationCombineService.getCombinedUserInfo(userId); + return new RecordsRes>(List.of(result)); // 明确指定类型 + } + + @PostMapping("/save") + public IdRes saveUserCombinedInfo(@RequestBody @Valid UserInfoCombineReq req) { + return new IdRes(userInformationCombineService.saveCombinedInfo(req)); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fhsmsc/modules/user_information/web/user_informationController.java b/src/main/java/com/ffii/fhsmsc/modules/user_information/web/user_informationController.java new file mode 100644 index 0000000..42d0244 --- /dev/null +++ b/src/main/java/com/ffii/fhsmsc/modules/user_information/web/user_informationController.java @@ -0,0 +1,49 @@ +package com.ffii.fhsmsc.modules.user_information.web; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ffii.core.response.IdRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; +import com.ffii.fhsmsc.modules.user_information.req.Saveuser_informationReq; +import com.ffii.fhsmsc.modules.user_information.service.user_informationService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/user_information") +public class user_informationController { + private static final Logger logger = LoggerFactory.getLogger(user_informationController.class); + private user_informationService user_informationService; + + public user_informationController( + user_informationService user_informationService + ) { + this.user_informationService = user_informationService; + } + + @GetMapping("/list") + public RecordsRes> listJson(HttpServletRequest request) throws ServletRequestBindingException { + logger.info("Received list request with parameters: {}", request.getParameterMap()); + Map args = CriteriaArgsBuilder.withRequest(request) + .addInteger("user_id") + .build(); + logger.info("Built args: {}", args); + return new RecordsRes<>(user_informationService.search(args)); + } + + @PostMapping("/save") + public IdRes saveOrUpdate(@RequestBody @Valid Saveuser_informationReq req) { + return new IdRes(user_informationService.saveOrUpdate(req).getId()); + } +} diff --git a/src/main/resources/application-db-anna.yml b/src/main/resources/application-db-anna.yml new file mode 100644 index 0000000..55fe7cd --- /dev/null +++ b/src/main/resources/application-db-anna.yml @@ -0,0 +1,11 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://192.168.1.228:3306/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + driver-class-name: com.mysql.cj.jdbc.Driver + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/src/main/resources/application-db-local.yml b/src/main/resources/application-db-local.yml new file mode 100644 index 0000000..220285b --- /dev/null +++ b/src/main/resources/application-db-local.yml @@ -0,0 +1,10 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://127.0.0.1:3308/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/src/main/resources/application-db-prod.yml b/src/main/resources/application-db-prod.yml new file mode 100644 index 0000000..a1509bf --- /dev/null +++ b/src/main/resources/application-db-prod.yml @@ -0,0 +1,11 @@ + +spring: + datasource: + jdbc-url: jdbc:mysql://127.0.0.1:3306/fhsmscdb?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8 + username: root + password: secret + driver-class-name: com.mysql.cj.jdbc.Driver + springdoc: + api-docs: + path: /v3/api-docs + enabled: true diff --git a/src/main/resources/application-ldap-prod.yml b/src/main/resources/application-ldap-prod.yml new file mode 100644 index 0000000..6974913 --- /dev/null +++ b/src/main/resources/application-ldap-prod.yml @@ -0,0 +1,9 @@ +spring: + ldap: + embedded: + port: 8389 + base-dn: dc=springframework,dc=org + ldif: classpath:ldap-test-users.ldif + validation: + enabled: false + urls: ldap://localhost:8389 \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..5997a49 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,2 @@ +host: + url: http://localhost:3000 \ No newline at end of file diff --git a/src/main/resources/application-prod-linux.yml b/src/main/resources/application-prod-linux.yml new file mode 100644 index 0000000..41ac797 --- /dev/null +++ b/src/main/resources/application-prod-linux.yml @@ -0,0 +1,2 @@ +logging: + config: 'classpath:log4j2-prod-linux.yml' \ No newline at end of file diff --git a/src/main/resources/application-prod-win.yml b/src/main/resources/application-prod-win.yml new file mode 100644 index 0000000..b1a50a9 --- /dev/null +++ b/src/main/resources/application-prod-win.yml @@ -0,0 +1,4 @@ +server: + port: 8087 +logging: + config: 'classpath:log4j2-prod-win.yml' \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..70f858c --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,28 @@ +server: + servlet: + contextPath: /api + encoding: + charset: UTF-8 + enabled: true + force: true + port: 8090 + error: + include-message: always + tomcat: + connection-timeout: 300000 +spring: + servlet: + multipart: + max-file-size: 500MB + max-request-size: 600MB + jpa: + hibernate: + naming: + physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + database-platform: org.hibernate.dialect.MySQL8Dialect + properties: + hibernate: + dialect: + storage_engine: innodb +logging: + config: 'classpath:log4j2.yml' \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/00_system_base/01_create_table.sql b/src/main/resources/db/changelog/changes/00_system_base/01_create_table.sql new file mode 100644 index 0000000..cc658c8 --- /dev/null +++ b/src/main/resources/db/changelog/changes/00_system_base/01_create_table.sql @@ -0,0 +1,385 @@ +--liquibase formatted sql + +--changeset terence:create table +--comment: create table + +-- +-- Table structure for table `audit_log` +-- + +DROP TABLE IF EXISTS `audit_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `audit_log` ( + `tableName` varchar(30) NOT NULL, + `recordId` int NOT NULL, + `recordName` varchar(500) DEFAULT NULL, + `modifiedBy` int DEFAULT NULL, + `modified` datetime DEFAULT NULL, + `oldData` json DEFAULT NULL, + `newData` json DEFAULT NULL, + KEY `idx_tableName_recordId` (`tableName`,`recordId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `audit_log` +-- + +LOCK TABLES `audit_log` WRITE; +/*!40000 ALTER TABLE `audit_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `audit_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `authority` +-- + +DROP TABLE IF EXISTS `authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(255) NOT NULL, + `name` varchar(100) NOT NULL, + `module` varchar(50) DEFAULT NULL, + `description` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `authority` +-- + +LOCK TABLES `authority` WRITE; +/*!40000 ALTER TABLE `authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file` +-- + +DROP TABLE IF EXISTS `file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `filename` varchar(255) NOT NULL, + `skey` varchar(50) NOT NULL, + `extension` varchar(10) DEFAULT NULL, + `mimetype` varchar(100) DEFAULT NULL, + `filesize` int DEFAULT NULL, + `remarks` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file` +-- + +LOCK TABLES `file` WRITE; +/*!40000 ALTER TABLE `file` DISABLE KEYS */; +/*!40000 ALTER TABLE `file` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file_blob` +-- + +DROP TABLE IF EXISTS `file_blob`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file_blob` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `fileId` int NOT NULL, + `bytes` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file_blob` +-- + +LOCK TABLES `file_blob` WRITE; +/*!40000 ALTER TABLE `file_blob` DISABLE KEYS */; +/*!40000 ALTER TABLE `file_blob` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `file_ref` +-- + +DROP TABLE IF EXISTS `file_ref`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `file_ref` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `fileId` int DEFAULT NULL, + `refCode` varchar(50) DEFAULT NULL, + `refId` int NOT NULL, + `refType` varchar(50) NOT NULL, + `remarks` varchar(255) DEFAULT NULL, + `thumbnailFileId` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `file_ref` +-- + +LOCK TABLES `file_ref` WRITE; +/*!40000 ALTER TABLE `file_ref` DISABLE KEYS */; +/*!40000 ALTER TABLE `file_ref` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group` +-- + +DROP TABLE IF EXISTS `group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `group` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `name` varchar(50) NOT NULL, + `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group` +-- + +LOCK TABLES `group` WRITE; +/*!40000 ALTER TABLE `group` DISABLE KEYS */; +/*!40000 ALTER TABLE `group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_authority` +-- + +DROP TABLE IF EXISTS `group_authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `group_authority` ( + `groupId` int NOT NULL, + `authId` int NOT NULL, + PRIMARY KEY (`groupId`,`authId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `group_authority` +-- + +LOCK TABLES `group_authority` WRITE; +/*!40000 ALTER TABLE `group_authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `group_authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `settings` +-- + +DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `settings` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `value` text NOT NULL, + `category` varchar(50) DEFAULT NULL, + `type` varchar(10) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `name_idx` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `settings` +-- + +LOCK TABLES `settings` WRITE; +/*!40000 ALTER TABLE `settings` DISABLE KEYS */; +/*!40000 ALTER TABLE `settings` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `username` varchar(30) NOT NULL, + `password` varchar(60) DEFAULT NULL, + `locked` tinyint(1) NOT NULL DEFAULT '0', + `expiryDate` date DEFAULT NULL, + `name` varchar(50) NOT NULL, + `locale` varchar(5) DEFAULT NULL, + `fullname` varchar(90) DEFAULT NULL, + `firstname` varchar(45) DEFAULT NULL, + `lastname` varchar(30) DEFAULT NULL, + `title` varchar(60) DEFAULT NULL, + `department` varchar(60) DEFAULT NULL, + `email` varchar(120) DEFAULT NULL, + `phone1` varchar(30) DEFAULT NULL, + `phone2` varchar(30) DEFAULT NULL, + `remarks` varchar(600) DEFAULT NULL, + `post` varchar(50) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user` +-- + +LOCK TABLES `user` WRITE; +/*!40000 ALTER TABLE `user` DISABLE KEYS */; +/*!40000 ALTER TABLE `user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_authority` +-- + +DROP TABLE IF EXISTS `user_authority`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_authority` ( + `userId` int NOT NULL, + `authId` int NOT NULL, + PRIMARY KEY (`userId`,`authId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_authority` +-- + +LOCK TABLES `user_authority` WRITE; +/*!40000 ALTER TABLE `user_authority` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_group` +-- + +DROP TABLE IF EXISTS `user_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_group` ( + `groupId` int NOT NULL, + `userId` int NOT NULL, + PRIMARY KEY (`groupId`,`userId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_group` +-- + +LOCK TABLES `user_group` WRITE; +/*!40000 ALTER TABLE `user_group` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_login_log` +-- + +DROP TABLE IF EXISTS `user_login_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_login_log` ( + `username` varchar(32) NOT NULL, + `loginTime` datetime NOT NULL, + `ipAddr` varchar(45) NOT NULL, + `success` tinyint(1) NOT NULL, + PRIMARY KEY (`username`,`loginTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_login_log` +-- + +LOCK TABLES `user_login_log` WRITE; +/*!40000 ALTER TABLE `user_login_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_login_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_password_history` +-- + +DROP TABLE IF EXISTS `user_password_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_password_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + `userId` int NOT NULL, + `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `password` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_password_history` +-- + +LOCK TABLES `user_password_history` WRITE; +/*!40000 ALTER TABLE `user_password_history` DISABLE KEYS */; +/*!40000 ALTER TABLE `user_password_history` ENABLE KEYS */; +UNLOCK TABLES; + diff --git a/src/main/resources/db/changelog/changes/00_system_base/02_insert_group_authority.sql b/src/main/resources/db/changelog/changes/00_system_base/02_insert_group_authority.sql new file mode 100644 index 0000000..3da0f9d --- /dev/null +++ b/src/main/resources/db/changelog/changes/00_system_base/02_insert_group_authority.sql @@ -0,0 +1,32 @@ +--liquibase formatted sql + +--changeset terence:insert group authority +--comment: insert group authority + +LOCK TABLES `authority` WRITE; +/*!40000 ALTER TABLE `authority` DISABLE KEYS */; +INSERT INTO `authority` VALUES (1,'MAINTAIN_USER_GROUP','Maintain User Group','System Administration','Allow to maintain user group'),(2,'MAINTAIN_USER','Maintain User','System Administration','Allow to maintain user'),(3,'VIEW_AUDIT_LOG','View Audit Log','System Administration','Allow to view audit log'),(4,'VIEW_LOGIN_LOG','View Login Log','System Administration','Allow to view login log'),(5,'MANAGE_PASSWORD_POLICY','Manage Password Policy','System Administration','Allow to manage password policy'),(6,'MANAGE_SYSTEM_CONFIGURATION','Manage System Configuration','System Administration','Allow to manage system configuration'); +/*!40000 ALTER TABLE `authority` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group` +-- + +LOCK TABLES `group` WRITE; +/*!40000 ALTER TABLE `group` DISABLE KEYS */; +INSERT INTO `group` VALUES + (1,NOW(),1,0,NOW(),1,0,'System Administrator','System Administrator Group') + ; +/*!40000 ALTER TABLE `group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `group_authority` +-- + +LOCK TABLES `group_authority` WRITE; +/*!40000 ALTER TABLE `group_authority` DISABLE KEYS */; +INSERT INTO `group_authority` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6); +/*!40000 ALTER TABLE `group_authority` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/src/main/resources/db/changelog/changes/00_system_base/03_insert_setting.sql b/src/main/resources/db/changelog/changes/00_system_base/03_insert_setting.sql new file mode 100644 index 0000000..8264c84 --- /dev/null +++ b/src/main/resources/db/changelog/changes/00_system_base/03_insert_setting.sql @@ -0,0 +1,10 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +LOCK TABLES `settings` WRITE; +/*!40000 ALTER TABLE `settings` DISABLE KEYS */; +INSERT INTO `settings` VALUES (1,'SYS.idleLogoutTime','60','settings','integer'),(2,'MAIL.smtp.host','','settings','string'),(3,'MAIL.smtp.port','25','settings','integer'),(4,'MAIL.smtp.username','','settings','string'),(5,'MAIL.smtp.password','','settings','string'),(6,'MAIL.smtp.auth','','settings','boolean'),(7,'SYS.password.rule.length.min','10','passwordPolicy','integer'),(8,'SYS.password.rule.duration','180','passwordPolicy','integer'),(9,'SYS.password.rule.history','8','passwordPolicy','integer'),(10,'SYS.loginAttempt.limit','5','passwordPolicy','integer'),(11,'SYS.loginAttempt.penalityTime','5','passwordPolicy','integer'),(12,'SYS.password.rule.numberAndAlphabetic','true','passwordPolicy','boolean'),(13,'SYS.password.rule.specialCharacter','true','passwordPolicy','boolean'),(14,'SYS.password.rule.notContainUsername','true','passwordPolicy','boolean'),(15,'SYS.password.rule.notContainThreeConsecutiveCharacters','true','passwordPolicy','boolean'); +/*!40000 ALTER TABLE `settings` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/src/main/resources/db/changelog/changes/00_system_base/04_create_user(optional).sql b/src/main/resources/db/changelog/changes/00_system_base/04_create_user(optional).sql new file mode 100644 index 0000000..9268e08 --- /dev/null +++ b/src/main/resources/db/changelog/changes/00_system_base/04_create_user(optional).sql @@ -0,0 +1,8 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +INSERT INTO `user` ( `id`, `created`, `createdBy`, `version`, `modified`, `modifiedBy`, `deleted`, `username`, `password`, `locked`, `expiryDate`, `name`, `locale`, `fullname`, `firstname`, `lastname`, `title`, `department`, `email`, `phone1`, `phone2`, `remarks`, `post` ) VALUES ( '1', NOW(), '1', '0', NOW(), '1', '0', '2fi', '$2a$10$cRJ.TOZPExg49qk.zmJlruW/Rj7X05vVMxD1CgAOOegZMsC1IPCFm', '0', NULL, '2fi', NULL, '2fi', NULL, NULL, NULL, NULL, '', '', NULL, NULL, '' ); + +INSERT INTO `user_group` (`groupId`, `userId`) VALUES ('1', '1'); diff --git a/src/main/resources/db/changelog/changes/enson_change/04_create_user(enson).sql b/src/main/resources/db/changelog/changes/enson_change/04_create_user(enson).sql new file mode 100644 index 0000000..dbed519 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/04_create_user(enson).sql @@ -0,0 +1,8 @@ +--liquibase formatted sql + +--changeset terence:insert setting +--comment: insert setting + +INSERT INTO `user` ( `id`, `created`, `createdBy`, `version`, `modified`, `modifiedBy`, `deleted`, `username`, `password`, `locked`, `expiryDate`, `name`, `locale`, `fullname`, `firstname`, `lastname`, `title`, `department`, `email`, `phone1`, `phone2`, `remarks`, `post` ) VALUES ( '2', NOW(), '2', '0', NOW(), '2', '0', '2fi2', '1234', '0', NULL, '2fi2', NULL, '2fi2', NULL, NULL, NULL, NULL, '', '', NULL, NULL, '' ); + +INSERT INTO `user_group` (`groupId`, `userId`) VALUES ('2', '2'); diff --git a/src/main/resources/db/changelog/changes/enson_change/05_create_food1.sql b/src/main/resources/db/changelog/changes/enson_change/05_create_food1.sql new file mode 100644 index 0000000..aed6f3e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/05_create_food1.sql @@ -0,0 +1,54 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `food1` ( + `id` int(11) NOT NULL, + `name` varchar(100) NOT NULL, + `kcal` decimal(10,2) DEFAULT NULL, + `protein` decimal(10,2) NOT NULL DEFAULT 0.00, + `fat` decimal(10,2) NOT NULL DEFAULT 0.00, + `satFat` decimal(10,2) NOT NULL DEFAULT 0.00, + `carbo` decimal(10,2) NOT NULL DEFAULT 0.00, + `sodium` decimal(10,2) NOT NULL DEFAULT 0.00, + `Ingredients` varchar(255) DEFAULT NULL, + `food_type` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +INSERT INTO `food1` (`id`, `name`, `kcal`, `protein`, `fat`, `satFat`, `carbo`, `sodium`, `Ingredients`, `food_type`) VALUES +(1, 'Apple', 52.00, 0.00, 0.00, 0.00, 14.00, 0.00, NULL, 'fruit'), +(2, 'Banana', 105.00, 1.30, 0.30, 0.10, 27.00, 1.00, NULL, 'fruit'), +(3, 'Chicken breast', 165.00, 31.00, 6.10, 2.00, 1.00, 69.00, NULL, 'food'), +(4, 'Brown rice', 216.00, 5.00, 3.00, 0.40, 45.00, 0.00, NULL, 'food'), +(5, 'Yogurt', 148.00, 5.20, 6.10, 3.40, 10.00, 125.00, NULL, 'food'), +(6, 'Egg', 78.00, 6.30, 5.60, 1.60, 0.40, 62.00, NULL, 'food'), +(7, 'Avocado', 160.00, 2.00, 14.60, 2.00, 8.50, 7.00, NULL, 'fruit'), +(8, 'Salmon', 206.00, 22.00, 13.60, 3.80, 5.00, 0.00, NULL, 'food'), +(9, 'Spinach', 23.00, 2.70, 0.40, 0.10, 3.60, 79.00, NULL, 'vegetable'), +(10, 'Carrot', 41.00, 0.90, 0.20, 0.00, 9.60, 69.00, NULL, 'vegetable'), +(11, 'Broccoli', 34.00, 2.80, 0.60, 0.20, 6.60, 66.00, NULL, 'vegetable'), +(12, 'Potato', 77.00, 2.00, 0.10, 0.00, 17.30, 10.00, NULL, 'vegetable'), +(13, 'Sweet potato', 101.00, 1.60, 0.10, 0.00, 22.00, 3.00, NULL, 'vegetable'), +(14, 'Onion', 40.00, 1.40, 0.10, 0.00, 9.30, 3.00, NULL, 'vegetable'), +(15, 'Garlic', 140.00, 6.70, 15.40, 3.30, 6.20, 167.00, NULL, 'food'), +(16, 'Tomato', 18.00, 0.90, 0.20, 0.00, 3.90, 5.00, NULL, 'vegetable'), +(17, 'Cucumber', 16.00, 0.70, 0.10, 0.00, 3.80, 2.00, NULL, 'fruit'), +(18, 'Lettuce', 8.00, 0.80, 0.20, 0.10, 1.70, 5.00, NULL, 'vegetable'), +(19, 'Bread', 265.00, 9.40, 4.60, 1.30, 52.00, 150.00, NULL, 'food'), +(20, 'Pasta', 131.00, 5.50, 1.00, 0.20, 26.00, 0.00, NULL, 'food'), +(21, 'Cheese', 402.00, 20.60, 33.10, 23.20, 1.30, 0.00, NULL, 'food'), +(22, 'Milk', 50.00, 3.30, 3.30, 1.70, 4.80, 120.00, NULL, 'drink'), +(23, 'Beef', 250.00, 22.00, 21.00, 8.00, 0.00, 50.00, NULL, 'food'), +(24, 'Pork', 241.00, 15.10, 19.70, 7.30, 0.00, 49.00, NULL, 'food'), +(25, 'Lamb', 294.00, 17.50, 23.20, 9.20, 0.00, 0.00, NULL, 'food'), +(26, 'Chicken thigh', 197.00, 27.00, 14.90, 5.20, 0.00, 0.00, NULL, 'food'); + + +ALTER TABLE `food1` + ADD PRIMARY KEY (`id`); + +ALTER TABLE `food1` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=27; +COMMIT; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/06_create_food_information.sql b/src/main/resources/db/changelog/changes/enson_change/06_create_food_information.sql new file mode 100644 index 0000000..4420cb9 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/06_create_food_information.sql @@ -0,0 +1,19 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `food_items` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + `kcal` float DEFAULT NULL, + `protein` float DEFAULT NULL, + `fat` float DEFAULT NULL, + `sat_fat` float DEFAULT NULL, + `carbo` float DEFAULT NULL, + `sodium` float DEFAULT NULL, + `date` datetime DEFAULT NULL, + `size` float DEFAULT NULL, + `meal` varchar(50) DEFAULT NULL, + `user_id` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/07_alter_food_information.sql b/src/main/resources/db/changelog/changes/enson_change/07_alter_food_information.sql new file mode 100644 index 0000000..214f255 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/07_alter_food_information.sql @@ -0,0 +1,352 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Alter table and insert data + +INSERT INTO `food_items` (`id`, `name`, `kcal`, `protein`, `fat`, `sat_fat`, `carbo`, `sodium`, `date`, `size`, `meal`, `user_id`) VALUES +(4, 'Banana', 105, 1.3, 0.3, 0.1, 27, 1, '2024-11-12 03:36:53', 100, 'breakfast', 1), +(5, 'Banana', 105, 1.3, 0.3, 0.1, 27, 1, '2024-11-10 04:04:28', 100, 'breakfast', 2), +(7, 'Banana', 9450, 117, 27, 9, 2430, 90, '2024-11-11 14:20:39', 9000, 'breakfast', 1), +(16, 'Salmon', 206, 22, 13.6, 3.8, 5, 0, '2024-11-14 02:02:19', 100, 'Dinner', 1), +(21, 'Pork', 180.8, 11.3, 14.8, 5.5, 0, 36.8, '2024-11-04 06:09:00', 75, 'Breakfast', 1), +(22, 'Pork', 337.4, 21.1, 27.6, 10.2, 0, 68.6, '2024-11-04 13:06:00', 140, 'Lunch', 1), +(23, 'Bread', 532.6, 18.9, 9.2, 2.6, 104.5, 301.5, '2024-11-04 17:28:00', 201, 'Dinner', 1), +(24, 'Garlic', 436.8, 20.9, 48, 10.3, 19.3, 521, '2024-11-04 17:28:00', 312, 'Dinner', 1), +(25, 'Bread', 1240.2, 44, 21.5, 6.1, 243.4, 702, '2024-11-05 06:40:00', 468, 'Breakfast', 1), +(26, 'Cucumber', 26.4, 1.2, 0.2, 0, 6.3, 3.3, '2024-11-05 06:40:00', 165, 'Breakfast', 1), +(27, 'Apple', 86.8, 0, 0, 0, 23.4, 0, '2024-11-05 12:35:00', 167, 'Lunch', 1), +(28, 'Bread', 638.7, 22.7, 11.1, 3.1, 125.3, 361.5, '2024-11-05 12:35:00', 241, 'Lunch', 1), +(29, 'Milk', 63.5, 4.2, 4.2, 2.2, 6.1, 152.4, '2024-11-05 17:36:00', 127, 'Dinner', 1), +(30, 'Broccoli', 31.6, 2.6, 0.6, 0.2, 6.1, 61.4, '2024-11-06 08:02:00', 93, 'Breakfast', 1), +(31, 'Potato', 184, 4.8, 0.2, 0, 41.3, 23.9, '2024-11-06 11:54:00', 239, 'Lunch', 1), +(32, 'Brown rice', 395.3, 9.2, 5.5, 0.7, 82.4, 0, '2024-11-06 11:54:00', 183, 'Lunch', 1), +(33, 'Yogurt', 130.2, 4.6, 5.4, 3, 8.8, 110, '2024-11-06 17:23:00', 88, 'Dinner', 1), +(34, 'Milk', 235, 15.5, 15.5, 8, 22.6, 564, '2024-11-07 06:00:00', 470, 'Breakfast', 1), +(35, 'Pasta', 279, 11.7, 2.1, 0.4, 55.4, 0, '2024-11-07 06:00:00', 213, 'Breakfast', 1), +(36, 'Lamb', 385.1, 22.9, 30.4, 12.1, 0, 0, '2024-11-07 13:43:00', 131, 'Lunch', 1), +(37, 'Yogurt', 497.3, 17.5, 20.5, 11.4, 33.6, 420, '2024-11-07 13:43:00', 336, 'Lunch', 1), +(38, 'Egg', 122.5, 9.9, 8.8, 2.5, 0.6, 97.3, '2024-11-07 19:13:00', 157, 'Dinner', 1), +(39, 'Carrot', 89, 2, 0.4, 0, 20.8, 149.7, '2024-11-07 19:13:00', 217, 'Dinner', 1), +(40, 'Garlic', 680.4, 32.6, 74.8, 16, 30.1, 811.6, '2024-11-08 08:52:00', 486, 'Breakfast', 1), +(41, 'Egg', 79.6, 6.4, 5.7, 1.6, 0.4, 63.2, '2024-11-08 08:52:00', 102, 'Breakfast', 1), +(42, 'Potato', 63.9, 1.7, 0.1, 0, 14.4, 8.3, '2024-11-08 13:22:00', 83, 'Lunch', 1), +(43, 'Salmon', 984.7, 105.2, 65, 18.2, 23.9, 0, '2024-11-08 19:18:00', 478, 'Dinner', 1), +(44, 'Tomato', 86, 4.3, 1, 0, 18.6, 23.9, '2024-11-09 06:19:00', 478, 'Breakfast', 1), +(45, 'Garlic', 518, 24.8, 57, 12.2, 22.9, 617.9, '2024-11-09 11:32:00', 370, 'Lunch', 1), +(46, 'Beef', 907.5, 79.9, 76.2, 29, 0, 181.5, '2024-11-09 11:32:00', 363, 'Lunch', 1), +(47, 'Beef', 517.5, 45.5, 43.5, 16.6, 0, 103.5, '2024-11-09 18:54:00', 207, 'Dinner', 1), +(48, 'Brown rice', 658.8, 15.2, 9.1, 1.2, 137.2, 0, '2024-11-09 18:54:00', 305, 'Dinner', 1), +(49, 'Egg', 69.4, 5.6, 5, 1.4, 0.4, 55.2, '2024-11-10 08:40:00', 89, 'Breakfast', 1), +(50, 'Apple', 90, 0, 0, 0, 24.2, 0, '2024-11-10 11:17:00', 173, 'Lunch', 1), +(51, 'Chicken breast', 227.7, 42.8, 8.4, 2.8, 0, 95.2, '2024-11-10 11:17:00', 138, 'Lunch', 1), +(52, 'Brown rice', 561.6, 13, 7.8, 1, 117, 0, '2024-11-10 17:05:00', 260, 'Dinner', 1), +(53, 'Spinach', 85.3, 10, 1.5, 0.4, 13.4, 293.1, '2024-11-04 08:47:00', 371, 'Breakfast', 2), +(54, 'Potato', 224.8, 5.8, 0.3, 0, 50.5, 29.2, '2024-11-04 08:47:00', 292, 'Breakfast', 2), +(55, 'Pork', 282, 17.7, 23, 8.5, 0, 57.3, '2024-11-04 13:48:00', 117, 'Lunch', 2), +(56, 'Cheese', 695.5, 35.6, 57.3, 40.1, 2.2, 0, '2024-11-04 19:05:00', 173, 'Dinner', 2), +(57, 'Cucumber', 16, 0.7, 0.1, 0, 3.8, 2, '2024-11-05 07:37:00', 100, 'Breakfast', 2), +(58, 'Cheese', 1523.6, 78.1, 125.4, 87.9, 4.9, 0, '2024-11-05 07:37:00', 379, 'Breakfast', 2), +(59, 'Chicken thigh', 374.3, 51.3, 28.3, 9.9, 0, 0, '2024-11-05 13:53:00', 190, 'Lunch', 2), +(60, 'Chicken thigh', 439.3, 60.2, 33.2, 11.6, 0, 0, '2024-11-05 13:53:00', 223, 'Lunch', 2), +(61, 'Garlic', 520.8, 24.9, 57.3, 12.3, 23.1, 621.2, '2024-11-05 18:09:00', 372, 'Dinner', 2), +(62, 'Apple', 210.6, 0, 0, 0, 56.7, 0, '2024-11-05 18:09:00', 405, 'Dinner', 2), +(63, 'Pasta', 191.3, 8, 1.5, 0.3, 38, 0, '2024-11-06 08:19:00', 146, 'Breakfast', 2), +(64, 'Yogurt', 338.9, 11.9, 14, 7.8, 22.9, 286.2, '2024-11-06 13:41:00', 229, 'Lunch', 2), +(65, 'Cucumber', 41.9, 1.8, 0.3, 0, 10, 5.2, '2024-11-06 13:41:00', 262, 'Lunch', 2), +(66, 'Potato', 170.2, 4.4, 0.2, 0, 38.2, 22.1, '2024-11-06 17:15:00', 221, 'Dinner', 2), +(67, 'Carrot', 125.5, 2.8, 0.6, 0, 29.4, 211.1, '2024-11-06 17:15:00', 306, 'Dinner', 2), +(68, 'Chicken thigh', 496.4, 68, 37.5, 13.1, 0, 0, '2024-11-07 08:03:00', 252, 'Breakfast', 2), +(69, 'Egg', 59.3, 4.8, 4.3, 1.2, 0.3, 47.1, '2024-11-07 08:03:00', 76, 'Breakfast', 2), +(70, 'Onion', 117.2, 4.1, 0.3, 0, 27.2, 8.8, '2024-11-07 12:33:00', 293, 'Lunch', 2), +(71, 'Pasta', 119.2, 5, 0.9, 0.2, 23.7, 0, '2024-11-07 12:33:00', 91, 'Lunch', 2), +(72, 'Carrot', 81.2, 1.8, 0.4, 0, 19, 136.6, '2024-11-07 17:09:00', 198, 'Dinner', 2), +(73, 'Spinach', 70.4, 8.3, 1.2, 0.3, 11, 241.7, '2024-11-08 07:29:00', 306, 'Breakfast', 2), +(74, 'Bread', 1240.2, 44, 21.5, 6.1, 243.4, 702, '2024-11-08 07:29:00', 468, 'Breakfast', 2), +(75, 'Apple', 225.7, 0, 0, 0, 60.8, 0, '2024-11-08 13:24:00', 434, 'Lunch', 2), +(76, 'Garlic', 491.4, 23.5, 54.1, 11.6, 21.8, 586.2, '2024-11-08 13:24:00', 351, 'Lunch', 2), +(77, 'Chicken breast', 183.2, 34.4, 6.8, 2.2, 0, 76.6, '2024-11-08 18:06:00', 111, 'Dinner', 2), +(78, 'Banana', 487.2, 6, 1.4, 0.5, 125.3, 4.6, '2024-11-08 18:06:00', 464, 'Dinner', 2), +(79, 'Tomato', 21.2, 1.1, 0.2, 0, 4.6, 5.9, '2024-11-09 06:11:00', 118, 'Breakfast', 2), +(80, 'Spinach', 110.9, 13, 1.9, 0.5, 17.4, 380.8, '2024-11-09 06:11:00', 482, 'Breakfast', 2), +(81, 'Cucumber', 68, 3, 0.4, 0, 16.1, 8.5, '2024-11-09 12:33:00', 425, 'Lunch', 2), +(82, 'Chicken breast', 189.7, 35.6, 7, 2.3, 0, 79.3, '2024-11-09 17:53:00', 115, 'Dinner', 2), +(83, 'Lamb', 1396.5, 83.1, 110.2, 43.7, 0, 0, '2024-11-10 08:16:00', 475, 'Breakfast', 2), +(84, 'Lamb', 1437.7, 85.6, 113.4, 45, 0, 0, '2024-11-10 12:08:00', 489, 'Lunch', 2), +(85, 'Bread', 561.8, 19.9, 9.8, 2.8, 110.2, 318, '2024-11-10 12:08:00', 212, 'Lunch', 2), +(86, 'Chicken thigh', 478.7, 65.6, 36.2, 12.6, 0, 0, '2024-11-10 18:47:00', 243, 'Dinner', 2), +(87, 'Apple', 43.2, 0, 0, 0, 11.6, 0, '2024-11-04 08:13:00', 83, 'Breakfast', 3), +(88, 'Garlic', 109.2, 5.2, 12, 2.6, 4.8, 130.3, '2024-11-04 11:22:00', 78, 'Lunch', 3), +(89, 'Broccoli', 158.8, 13.1, 2.8, 0.9, 30.8, 308.2, '2024-11-04 11:22:00', 467, 'Lunch', 3), +(90, 'Milk', 200, 13.2, 13.2, 6.8, 19.2, 480, '2024-11-04 18:25:00', 400, 'Dinner', 3), +(91, 'Cheese', 1290.4, 66.1, 106.3, 74.5, 4.2, 0, '2024-11-05 07:51:00', 321, 'Breakfast', 3), +(92, 'Tomato', 71.6, 3.6, 0.8, 0, 15.5, 19.9, '2024-11-05 07:51:00', 398, 'Breakfast', 3), +(93, 'Lamb', 308.7, 18.4, 24.4, 9.7, 0, 0, '2024-11-05 13:43:00', 105, 'Lunch', 3), +(94, 'Yogurt', 464.7, 16.3, 19.2, 10.7, 31.4, 392.5, '2024-11-05 17:15:00', 314, 'Dinner', 3), +(95, 'Lamb', 726.2, 43.2, 57.3, 22.7, 0, 0, '2024-11-06 08:12:00', 247, 'Breakfast', 3), +(96, 'Beef', 607.5, 53.5, 51, 19.4, 0, 121.5, '2024-11-06 08:12:00', 243, 'Breakfast', 3), +(97, 'Garlic', 427, 20.4, 47, 10.1, 18.9, 509.3, '2024-11-06 11:43:00', 305, 'Lunch', 3), +(98, 'Salmon', 271.9, 29, 18, 5, 6.6, 0, '2024-11-06 19:18:00', 132, 'Dinner', 3), +(99, 'Salmon', 366.7, 39.2, 24.2, 6.8, 8.9, 0, '2024-11-07 07:44:00', 178, 'Breakfast', 3), +(100, 'Sweet potato', 404, 6.4, 0.4, 0, 88, 12, '2024-11-07 12:21:00', 400, 'Lunch', 3), +(101, 'Sweet potato', 101, 1.6, 0.1, 0, 22, 3, '2024-11-07 18:45:00', 100, 'Dinner', 3), +(102, 'Lettuce', 8.6, 0.9, 0.2, 0.1, 1.8, 5.4, '2024-11-08 08:42:00', 107, 'Breakfast', 3), +(103, 'Garlic', 233.8, 11.2, 25.7, 5.5, 10.4, 278.9, '2024-11-08 08:42:00', 167, 'Breakfast', 3), +(104, 'Milk', 207.5, 13.7, 13.7, 7.1, 19.9, 498, '2024-11-08 11:06:00', 415, 'Lunch', 3), +(105, 'Cheese', 747.7, 38.3, 61.6, 43.2, 2.4, 0, '2024-11-08 11:06:00', 186, 'Lunch', 3), +(106, 'Salmon', 828.1, 88.4, 54.7, 15.3, 20.1, 0, '2024-11-08 19:08:00', 402, 'Dinner', 3), +(107, 'Yogurt', 216.1, 7.6, 8.9, 5, 14.6, 182.5, '2024-11-08 19:08:00', 146, 'Dinner', 3), +(108, 'Bread', 816.2, 29, 14.2, 4, 160.2, 462, '2024-11-09 06:17:00', 308, 'Breakfast', 3), +(109, 'Onion', 190.4, 6.7, 0.5, 0, 44.3, 14.3, '2024-11-09 13:04:00', 476, 'Lunch', 3), +(110, 'Banana', 280.3, 3.5, 0.8, 0.3, 72.1, 2.7, '2024-11-09 13:04:00', 267, 'Lunch', 3), +(111, 'Avocado', 574.4, 7.2, 52.4, 7.2, 30.5, 25.1, '2024-11-09 17:53:00', 359, 'Dinner', 3), +(112, 'Carrot', 41.4, 0.9, 0.2, 0, 9.7, 69.7, '2024-11-10 06:57:00', 101, 'Breakfast', 3), +(113, 'Cheese', 1093.4, 56, 90, 63.1, 3.5, 0, '2024-11-10 11:51:00', 272, 'Lunch', 3), +(114, 'Pork', 253.1, 15.9, 20.7, 7.7, 0, 51.5, '2024-11-10 11:51:00', 105, 'Lunch', 3), +(115, 'Lettuce', 8.6, 0.9, 0.2, 0.1, 1.8, 5.4, '2024-11-10 17:02:00', 108, 'Dinner', 3), +(116, 'Milk', 98, 6.5, 6.5, 3.3, 9.4, 235.2, '2024-11-10 17:02:00', 196, 'Dinner', 3), +(117, 'Cucumber', 69.3, 3, 0.4, 0, 16.5, 8.7, '2024-11-04 07:44:00', 433, 'Breakfast', 4), +(118, 'Pasta', 482.1, 20.2, 3.7, 0.7, 95.7, 0, '2024-11-04 07:44:00', 368, 'Breakfast', 4), +(119, 'Lamb', 358.7, 21.3, 28.3, 11.2, 0, 0, '2024-11-04 11:37:00', 122, 'Lunch', 4), +(120, 'Pork', 257.9, 16.2, 21.1, 7.8, 0, 52.4, '2024-11-04 11:37:00', 107, 'Lunch', 4), +(121, 'Banana', 269.8, 3.3, 0.8, 0.3, 69.4, 2.6, '2024-11-04 18:32:00', 257, 'Dinner', 4), +(122, 'Tomato', 24.7, 1.2, 0.3, 0, 5.3, 6.9, '2024-11-04 18:32:00', 137, 'Dinner', 4), +(123, 'Yogurt', 384.8, 13.5, 15.9, 8.8, 26, 325, '2024-11-05 06:05:00', 260, 'Breakfast', 4), +(124, 'Yogurt', 380.4, 13.4, 15.7, 8.7, 25.7, 321.2, '2024-11-05 06:05:00', 257, 'Breakfast', 4), +(125, 'Lettuce', 17, 1.7, 0.4, 0.2, 3.6, 10.6, '2024-11-05 12:17:00', 212, 'Lunch', 4), +(126, 'Milk', 214, 14.1, 14.1, 7.3, 20.5, 513.6, '2024-11-05 12:17:00', 428, 'Lunch', 4), +(127, 'Egg', 244.9, 19.8, 17.6, 5, 1.3, 194.7, '2024-11-05 17:57:00', 314, 'Dinner', 4), +(128, 'Pork', 1185.7, 74.3, 96.9, 35.9, 0, 241.1, '2024-11-05 17:57:00', 492, 'Dinner', 4), +(129, 'Yogurt', 519.5, 18.3, 21.4, 11.9, 35.1, 438.8, '2024-11-06 06:03:00', 351, 'Breakfast', 4), +(130, 'Sweet potato', 502, 8, 0.5, 0, 109.3, 14.9, '2024-11-06 06:03:00', 497, 'Breakfast', 4), +(131, 'Bread', 1017.6, 36.1, 17.7, 5, 199.7, 576, '2024-11-06 12:56:00', 384, 'Lunch', 4), +(132, 'Lamb', 1237.7, 73.7, 97.7, 38.7, 0, 0, '2024-11-06 18:02:00', 421, 'Dinner', 4), +(133, 'Avocado', 384, 4.8, 35, 4.8, 20.4, 16.8, '2024-11-07 07:48:00', 240, 'Breakfast', 4), +(134, 'Milk', 103.5, 6.8, 6.8, 3.5, 9.9, 248.4, '2024-11-07 07:48:00', 207, 'Breakfast', 4), +(135, 'Broccoli', 124.8, 10.3, 2.2, 0.7, 24.2, 242.2, '2024-11-07 13:38:00', 367, 'Lunch', 4), +(136, 'Cheese', 932.6, 47.8, 76.8, 53.8, 3, 0, '2024-11-07 13:38:00', 232, 'Lunch', 4), +(137, 'Lettuce', 19.2, 1.9, 0.5, 0.2, 4.1, 12, '2024-11-07 17:46:00', 240, 'Dinner', 4), +(138, 'Lamb', 655.6, 39, 51.7, 20.5, 0, 0, '2024-11-07 17:46:00', 223, 'Dinner', 4), +(139, 'Chicken thigh', 922, 126.4, 69.7, 24.3, 0, 0, '2024-11-08 06:52:00', 468, 'Breakfast', 4), +(140, 'Chicken thigh', 165.5, 22.7, 12.5, 4.4, 0, 0, '2024-11-08 06:52:00', 84, 'Breakfast', 4), +(141, 'Brown rice', 607, 14.1, 8.4, 1.1, 126.5, 0, '2024-11-08 11:38:00', 281, 'Lunch', 4), +(142, 'Yogurt', 627.5, 22, 25.9, 14.4, 42.4, 530, '2024-11-08 18:25:00', 424, 'Dinner', 4), +(143, 'Chicken breast', 94, 17.7, 3.5, 1.1, 0, 39.3, '2024-11-09 08:14:00', 57, 'Breakfast', 4), +(144, 'Cucumber', 64.2, 2.8, 0.4, 0, 15.2, 8, '2024-11-09 11:24:00', 401, 'Lunch', 4), +(145, 'Cucumber', 20.5, 0.9, 0.1, 0, 4.9, 2.6, '2024-11-09 19:34:00', 128, 'Dinner', 4), +(146, 'Yogurt', 569.8, 20, 23.5, 13.1, 38.5, 481.2, '2024-11-09 19:34:00', 385, 'Dinner', 4), +(147, 'Egg', 172.4, 13.9, 12.4, 3.5, 0.9, 137, '2024-11-10 06:41:00', 221, 'Breakfast', 4), +(148, 'Milk', 218.5, 14.4, 14.4, 7.4, 21, 524.4, '2024-11-10 12:55:00', 437, 'Lunch', 4), +(149, 'Brown rice', 712.8, 16.5, 9.9, 1.3, 148.5, 0, '2024-11-10 12:55:00', 330, 'Lunch', 4), +(150, 'Garlic', 324.8, 15.5, 35.7, 7.7, 14.4, 387.4, '2024-11-10 18:24:00', 232, 'Dinner', 4), +(151, 'Potato', 57.8, 1.5, 0.1, 0, 13, 7.5, '2024-11-10 18:24:00', 75, 'Dinner', 4), +(152, 'Potato', 60.8, 1.6, 0.1, 0, 13.7, 7.9, '2024-11-04 07:23:00', 79, 'Breakfast', 5), +(153, 'Tomato', 85, 4.2, 0.9, 0, 18.4, 23.6, '2024-11-04 07:23:00', 472, 'Breakfast', 5), +(154, 'Sweet potato', 484.8, 7.7, 0.5, 0, 105.6, 14.4, '2024-11-04 11:51:00', 480, 'Lunch', 5), +(155, 'Lamb', 820.3, 48.8, 64.7, 25.7, 0, 0, '2024-11-04 17:50:00', 279, 'Dinner', 5), +(156, 'Pork', 301.2, 18.9, 24.6, 9.1, 0, 61.2, '2024-11-05 06:15:00', 125, 'Breakfast', 5), +(157, 'Bread', 1057.4, 37.5, 18.4, 5.2, 207.5, 598.5, '2024-11-05 06:15:00', 399, 'Breakfast', 5), +(158, 'Tomato', 85.9, 4.3, 1, 0, 18.6, 23.8, '2024-11-05 11:50:00', 477, 'Lunch', 5), +(159, 'Pork', 819.4, 51.3, 67, 24.8, 0, 166.6, '2024-11-05 11:50:00', 340, 'Lunch', 5), +(160, 'Egg', 48.4, 3.9, 3.5, 1, 0.2, 38.4, '2024-11-05 18:51:00', 62, 'Dinner', 5), +(161, 'Cucumber', 58.1, 2.5, 0.4, 0, 13.8, 7.3, '2024-11-05 18:51:00', 363, 'Dinner', 5), +(162, 'Garlic', 642.6, 30.8, 70.7, 15.1, 28.5, 766.5, '2024-11-06 08:26:00', 459, 'Breakfast', 5), +(163, 'Lettuce', 11, 1.1, 0.3, 0.1, 2.3, 6.9, '2024-11-06 08:26:00', 137, 'Breakfast', 5), +(164, 'Garlic', 253.4, 12.1, 27.9, 6, 11.2, 302.3, '2024-11-06 11:43:00', 181, 'Lunch', 5), +(165, 'Yogurt', 389.2, 13.7, 16, 8.9, 26.3, 328.8, '2024-11-06 11:43:00', 263, 'Lunch', 5), +(166, 'Beef', 1125, 99, 94.5, 36, 0, 225, '2024-11-06 18:56:00', 450, 'Dinner', 5), +(167, 'Brown rice', 382.3, 8.8, 5.3, 0.7, 79.7, 0, '2024-11-07 07:09:00', 177, 'Breakfast', 5), +(168, 'Avocado', 540.8, 6.8, 49.3, 6.8, 28.7, 23.7, '2024-11-07 13:16:00', 338, 'Lunch', 5), +(169, 'Broccoli', 154.7, 12.7, 2.7, 0.9, 30, 300.3, '2024-11-07 13:16:00', 455, 'Lunch', 5), +(170, 'Apple', 215.8, 0, 0, 0, 58.1, 0, '2024-11-07 18:54:00', 415, 'Dinner', 5), +(171, 'Spinach', 50.1, 5.9, 0.9, 0.2, 7.8, 172.2, '2024-11-08 07:10:00', 218, 'Breakfast', 5), +(172, 'Broccoli', 24.1, 2, 0.4, 0.1, 4.7, 46.9, '2024-11-08 11:41:00', 71, 'Lunch', 5), +(173, 'Milk', 189.5, 12.5, 12.5, 6.4, 18.2, 454.8, '2024-11-08 17:13:00', 379, 'Dinner', 5), +(174, 'Cheese', 1893.4, 97, 155.9, 109.3, 6.1, 0, '2024-11-08 17:13:00', 471, 'Dinner', 5), +(175, 'Salmon', 253.4, 27.1, 16.7, 4.7, 6.2, 0, '2024-11-09 07:15:00', 123, 'Breakfast', 5), +(176, 'Lettuce', 34.6, 3.5, 0.9, 0.4, 7.3, 21.6, '2024-11-09 07:15:00', 432, 'Breakfast', 5), +(177, 'Lettuce', 7.4, 0.7, 0.2, 0.1, 1.6, 4.7, '2024-11-09 12:38:00', 93, 'Lunch', 5), +(178, 'Spinach', 57.5, 6.8, 1, 0.2, 9, 197.5, '2024-11-09 12:38:00', 250, 'Lunch', 5), +(179, 'Cucumber', 27, 1.2, 0.2, 0, 6.4, 3.4, '2024-11-09 17:59:00', 169, 'Dinner', 5), +(180, 'Egg', 81.1, 6.6, 5.8, 1.7, 0.4, 64.5, '2024-11-09 17:59:00', 104, 'Dinner', 5), +(181, 'Tomato', 45.9, 2.3, 0.5, 0, 9.9, 12.8, '2024-11-10 07:50:00', 255, 'Breakfast', 5), +(182, 'Tomato', 81.9, 4.1, 0.9, 0, 17.7, 22.8, '2024-11-10 12:02:00', 455, 'Lunch', 5), +(183, 'Salmon', 379, 40.5, 25, 7, 9.2, 0, '2024-11-10 18:02:00', 184, 'Dinner', 5), +(184, 'Lamb', 308.7, 18.4, 24.4, 9.7, 0, 0, '2024-11-04 07:46:00', 105, 'Breakfast', 6), +(185, 'Lamb', 787.9, 46.9, 62.2, 24.7, 0, 0, '2024-11-04 07:46:00', 268, 'Breakfast', 6), +(186, 'Tomato', 76, 3.8, 0.8, 0, 16.5, 21.1, '2024-11-04 12:48:00', 422, 'Lunch', 6), +(187, 'Potato', 370.4, 9.6, 0.5, 0, 83.2, 48.1, '2024-11-04 12:48:00', 481, 'Lunch', 6), +(188, 'Lettuce', 21, 2.1, 0.5, 0.3, 4.5, 13.1, '2024-11-04 18:25:00', 263, 'Dinner', 6), +(189, 'Avocado', 552, 6.9, 50.4, 6.9, 29.3, 24.2, '2024-11-05 06:11:00', 345, 'Breakfast', 6), +(190, 'Carrot', 62.7, 1.4, 0.3, 0, 14.7, 105.6, '2024-11-05 11:02:00', 153, 'Lunch', 6), +(191, 'Yogurt', 134.7, 4.7, 5.6, 3.1, 9.1, 113.8, '2024-11-05 11:02:00', 91, 'Lunch', 6), +(192, 'Tomato', 78.8, 3.9, 0.9, 0, 17.1, 21.9, '2024-11-05 19:27:00', 438, 'Dinner', 6), +(193, 'Beef', 340, 29.9, 28.6, 10.9, 0, 68, '2024-11-05 19:27:00', 136, 'Dinner', 6), +(194, 'Onion', 108.4, 3.8, 0.3, 0, 25.2, 8.1, '2024-11-06 06:42:00', 271, 'Breakfast', 6), +(195, 'Egg', 350.2, 28.3, 25.1, 7.2, 1.8, 278.4, '2024-11-06 13:46:00', 449, 'Lunch', 6), +(196, 'Sweet potato', 494.9, 7.8, 0.5, 0, 107.8, 14.7, '2024-11-06 17:34:00', 490, 'Dinner', 6), +(197, 'Tomato', 83.7, 4.2, 0.9, 0, 18.1, 23.2, '2024-11-06 17:34:00', 465, 'Dinner', 6), +(198, 'Brown rice', 110.2, 2.5, 1.5, 0.2, 22.9, 0, '2024-11-07 06:48:00', 51, 'Breakfast', 6), +(199, 'Pasta', 615.7, 25.9, 4.7, 0.9, 122.2, 0, '2024-11-07 06:48:00', 470, 'Breakfast', 6), +(200, 'Potato', 150.2, 3.9, 0.2, 0, 33.7, 19.5, '2024-11-07 13:42:00', 195, 'Lunch', 6), +(201, 'Brown rice', 708.5, 16.4, 9.8, 1.3, 147.6, 0, '2024-11-07 13:42:00', 328, 'Lunch', 6), +(202, 'Lettuce', 25.9, 2.6, 0.6, 0.3, 5.5, 16.2, '2024-11-07 19:44:00', 324, 'Dinner', 6), +(203, 'Brown rice', 490.3, 11.3, 6.8, 0.9, 102.2, 0, '2024-11-08 08:13:00', 227, 'Breakfast', 6), +(204, 'Banana', 113.4, 1.4, 0.3, 0.1, 29.2, 1.1, '2024-11-08 08:13:00', 108, 'Breakfast', 6), +(205, 'Banana', 300.3, 3.7, 0.9, 0.3, 77.2, 2.9, '2024-11-08 12:25:00', 286, 'Lunch', 6), +(206, 'Pork', 848.3, 53.2, 69.3, 25.7, 0, 172.5, '2024-11-08 18:46:00', 352, 'Dinner', 6), +(207, 'Cucumber', 75.5, 3.3, 0.5, 0, 17.9, 9.4, '2024-11-09 07:57:00', 472, 'Breakfast', 6), +(208, 'Sweet potato', 361.6, 5.7, 0.4, 0, 78.8, 10.7, '2024-11-09 12:22:00', 358, 'Lunch', 6), +(209, 'Lettuce', 8.3, 0.8, 0.2, 0.1, 1.8, 5.2, '2024-11-09 12:22:00', 104, 'Lunch', 6), +(210, 'Pasta', 200.4, 8.4, 1.5, 0.3, 39.8, 0, '2024-11-09 17:26:00', 153, 'Dinner', 6), +(211, 'Pasta', 117.9, 5, 0.9, 0.2, 23.4, 0, '2024-11-10 07:29:00', 90, 'Breakfast', 6), +(212, 'Brown rice', 738.7, 17.1, 10.3, 1.4, 153.9, 0, '2024-11-10 07:29:00', 342, 'Breakfast', 6), +(213, 'Onion', 55.6, 1.9, 0.1, 0, 12.9, 4.2, '2024-11-10 11:18:00', 139, 'Lunch', 6), +(214, 'Potato', 338.8, 8.8, 0.4, 0, 76.1, 44, '2024-11-10 18:58:00', 440, 'Dinner', 6), +(215, 'Bread', 339.2, 12, 5.9, 1.7, 66.6, 192, '2024-11-10 18:58:00', 128, 'Dinner', 6), +(216, 'Carrot', 145.5, 3.2, 0.7, 0, 34.1, 244.9, '2024-11-04 06:16:00', 355, 'Breakfast', 7), +(217, 'Bread', 667.8, 23.7, 11.6, 3.3, 131, 378, '2024-11-04 13:58:00', 252, 'Lunch', 7), +(218, 'Tomato', 39.4, 2, 0.4, 0, 8.5, 10.9, '2024-11-04 13:58:00', 219, 'Lunch', 7), +(219, 'Cheese', 1234.1, 63.2, 101.6, 71.2, 4, 0, '2024-11-04 18:34:00', 307, 'Dinner', 7), +(220, 'Chicken thigh', 933.8, 128, 70.6, 24.6, 0, 0, '2024-11-04 18:34:00', 474, 'Dinner', 7), +(221, 'Banana', 155.4, 1.9, 0.4, 0.1, 40, 1.5, '2024-11-05 07:24:00', 148, 'Breakfast', 7), +(222, 'Yogurt', 464.7, 16.3, 19.2, 10.7, 31.4, 392.5, '2024-11-05 11:29:00', 314, 'Lunch', 7), +(223, 'Beef', 367.5, 32.3, 30.9, 11.8, 0, 73.5, '2024-11-05 11:29:00', 147, 'Lunch', 7), +(224, 'Yogurt', 122.8, 4.3, 5.1, 2.8, 8.3, 103.8, '2024-11-05 17:20:00', 83, 'Dinner', 7), +(225, 'Apple', 144, 0, 0, 0, 38.8, 0, '2024-11-06 07:30:00', 277, 'Breakfast', 7), +(226, 'Beef', 840, 73.9, 70.6, 26.9, 0, 168, '2024-11-06 07:30:00', 336, 'Breakfast', 7), +(227, 'Garlic', 522.2, 25, 57.4, 12.3, 23.1, 622.9, '2024-11-06 12:35:00', 373, 'Lunch', 7), +(228, 'Chicken thigh', 823.5, 112.9, 62.3, 21.7, 0, 0, '2024-11-06 12:35:00', 418, 'Lunch', 7), +(229, 'Salmon', 115.4, 12.3, 7.6, 2.1, 2.8, 0, '2024-11-06 17:54:00', 56, 'Dinner', 7), +(230, 'Tomato', 37.1, 1.9, 0.4, 0, 8, 10.3, '2024-11-07 06:25:00', 206, 'Breakfast', 7), +(231, 'Avocado', 537.6, 6.7, 49.1, 6.7, 28.6, 23.5, '2024-11-07 12:48:00', 336, 'Lunch', 7), +(232, 'Broccoli', 32, 2.6, 0.6, 0.2, 6.2, 62, '2024-11-07 12:48:00', 94, 'Lunch', 7), +(233, 'Brown rice', 477.4, 11.1, 6.6, 0.9, 99.5, 0, '2024-11-07 17:31:00', 221, 'Dinner', 7), +(234, 'Cucumber', 72.3, 3.2, 0.5, 0, 17.2, 9, '2024-11-08 07:09:00', 452, 'Breakfast', 7), +(235, 'Garlic', 512.4, 24.5, 56.4, 12.1, 22.7, 611.2, '2024-11-08 11:58:00', 366, 'Lunch', 7), +(236, 'Chicken thigh', 240.3, 32.9, 18.2, 6.3, 0, 0, '2024-11-08 11:58:00', 122, 'Lunch', 7), +(237, 'Onion', 137.2, 4.8, 0.3, 0, 31.9, 10.3, '2024-11-08 19:26:00', 343, 'Dinner', 7), +(238, 'Pork', 607.3, 38.1, 49.6, 18.4, 0, 123.5, '2024-11-08 19:26:00', 252, 'Dinner', 7), +(239, 'Banana', 297.2, 3.7, 0.8, 0.3, 76.4, 2.8, '2024-11-09 07:33:00', 283, 'Breakfast', 7), +(240, 'Chicken breast', 790.4, 148.5, 29.2, 9.6, 0, 330.5, '2024-11-09 07:33:00', 479, 'Breakfast', 7), +(241, 'Lamb', 311.6, 18.6, 24.6, 9.8, 0, 0, '2024-11-09 13:15:00', 106, 'Lunch', 7), +(242, 'Chicken thigh', 784.1, 107.5, 59.3, 20.7, 0, 0, '2024-11-09 13:15:00', 398, 'Lunch', 7), +(243, 'Apple', 116, 0, 0, 0, 31.2, 0, '2024-11-09 18:17:00', 223, 'Dinner', 7), +(244, 'Salmon', 945.5, 101, 62.4, 17.4, 22.9, 0, '2024-11-09 18:17:00', 459, 'Dinner', 7), +(245, 'Banana', 498.8, 6.2, 1.4, 0.5, 128.2, 4.8, '2024-11-10 06:40:00', 475, 'Breakfast', 7), +(246, 'Lamb', 1111.3, 66.1, 87.7, 34.8, 0, 0, '2024-11-10 11:17:00', 378, 'Lunch', 7), +(247, 'Avocado', 475.2, 5.9, 43.4, 5.9, 25.2, 20.8, '2024-11-10 11:17:00', 297, 'Lunch', 7), +(248, 'Banana', 183.8, 2.3, 0.5, 0.2, 47.2, 1.8, '2024-11-10 17:29:00', 175, 'Dinner', 7), +(249, 'Onion', 119.6, 4.2, 0.3, 0, 27.8, 9, '2024-11-04 07:58:00', 299, 'Breakfast', 8), +(250, 'Beef', 465, 40.9, 39.1, 14.9, 0, 93, '2024-11-04 13:10:00', 186, 'Lunch', 8), +(251, 'Bread', 871.9, 30.9, 15.1, 4.3, 171.1, 493.5, '2024-11-04 19:57:00', 329, 'Dinner', 8), +(252, 'Lamb', 884.9, 52.7, 69.8, 27.7, 0, 0, '2024-11-05 06:10:00', 301, 'Breakfast', 8), +(253, 'Milk', 230, 15.2, 15.2, 7.8, 22.1, 552, '2024-11-05 06:10:00', 460, 'Breakfast', 8), +(254, 'Lettuce', 35.8, 3.6, 0.9, 0.4, 7.6, 22.4, '2024-11-05 11:42:00', 448, 'Lunch', 8), +(255, 'Salmon', 786.9, 84, 52, 14.5, 19.1, 0, '2024-11-05 11:42:00', 382, 'Lunch', 8), +(256, 'Beef', 535, 47.1, 44.9, 17.1, 0, 107, '2024-11-05 19:16:00', 214, 'Dinner', 8), +(257, 'Cucumber', 71.2, 3.1, 0.4, 0, 16.9, 8.9, '2024-11-06 08:21:00', 445, 'Breakfast', 8), +(258, 'Chicken thigh', 396, 54.3, 29.9, 10.5, 0, 0, '2024-11-06 08:21:00', 201, 'Breakfast', 8), +(259, 'Chicken breast', 513.1, 96.4, 19, 6.2, 0, 214.6, '2024-11-06 12:27:00', 311, 'Lunch', 8), +(260, 'Sweet potato', 212.1, 3.4, 0.2, 0, 46.2, 6.3, '2024-11-06 18:02:00', 210, 'Dinner', 8), +(261, 'Chicken thigh', 439.3, 60.2, 33.2, 11.6, 0, 0, '2024-11-06 18:02:00', 223, 'Dinner', 8), +(262, 'Sweet potato', 211.1, 3.3, 0.2, 0, 46, 6.3, '2024-11-07 07:56:00', 209, 'Breakfast', 8), +(263, 'Pasta', 94.3, 4, 0.7, 0.1, 18.7, 0, '2024-11-07 07:56:00', 72, 'Breakfast', 8), +(264, 'Spinach', 74.5, 8.7, 1.3, 0.3, 11.7, 256, '2024-11-07 11:35:00', 324, 'Lunch', 8), +(265, 'Chicken breast', 669.9, 125.9, 24.8, 8.1, 0, 280.1, '2024-11-07 18:49:00', 406, 'Dinner', 8), +(266, 'Cheese', 888.4, 45.5, 73.2, 51.3, 2.9, 0, '2024-11-08 07:19:00', 221, 'Breakfast', 8), +(267, 'Potato', 168.6, 4.4, 0.2, 0, 37.9, 21.9, '2024-11-08 07:19:00', 219, 'Breakfast', 8), +(268, 'Banana', 203.7, 2.5, 0.6, 0.2, 52.4, 1.9, '2024-11-08 12:42:00', 194, 'Lunch', 8), +(269, 'Yogurt', 284.2, 10, 11.7, 6.5, 19.2, 240, '2024-11-08 12:42:00', 192, 'Lunch', 8), +(270, 'Sweet potato', 403, 6.4, 0.4, 0, 87.8, 12, '2024-11-08 18:37:00', 399, 'Dinner', 8), +(271, 'Chicken breast', 785.4, 147.6, 29, 9.5, 0, 328.4, '2024-11-09 07:08:00', 476, 'Breakfast', 8), +(272, 'Avocado', 612.8, 7.7, 55.9, 7.7, 32.6, 26.8, '2024-11-09 07:08:00', 383, 'Breakfast', 8), +(273, 'Egg', 131, 10.6, 9.4, 2.7, 0.7, 104.2, '2024-11-09 12:47:00', 168, 'Lunch', 8), +(274, 'Lettuce', 20.7, 2.1, 0.5, 0.3, 4.4, 12.9, '2024-11-09 12:47:00', 259, 'Lunch', 8), +(275, 'Salmon', 222.5, 23.8, 14.7, 4.1, 5.4, 0, '2024-11-09 18:20:00', 108, 'Dinner', 8), +(276, 'Spinach', 111.3, 13.1, 1.9, 0.5, 17.4, 382.4, '2024-11-10 06:56:00', 484, 'Breakfast', 8), +(277, 'Garlic', 460.6, 22, 50.7, 10.9, 20.4, 549.4, '2024-11-10 11:47:00', 329, 'Lunch', 8), +(278, 'Lamb', 643.9, 38.3, 50.8, 20.1, 0, 0, '2024-11-10 19:54:00', 219, 'Dinner', 8), +(279, 'Beef', 1125, 99, 94.5, 36, 0, 225, '2024-11-10 19:54:00', 450, 'Dinner', 8), +(280, 'Lamb', 929, 55.3, 73.3, 29.1, 0, 0, '2024-11-04 07:54:00', 316, 'Breakfast', 9), +(281, 'Pork', 443.4, 27.8, 36.2, 13.4, 0, 90.2, '2024-11-04 12:05:00', 184, 'Lunch', 9), +(282, 'Beef', 885, 77.9, 74.3, 28.3, 0, 177, '2024-11-04 12:05:00', 354, 'Lunch', 9), +(283, 'Brown rice', 276.5, 6.4, 3.8, 0.5, 57.6, 0, '2024-11-04 18:57:00', 128, 'Dinner', 9), +(284, 'Banana', 265.6, 3.3, 0.8, 0.3, 68.3, 2.5, '2024-11-04 18:57:00', 253, 'Dinner', 9), +(285, 'Bread', 1298.5, 46.1, 22.5, 6.4, 254.8, 735, '2024-11-05 07:40:00', 490, 'Breakfast', 9), +(286, 'Milk', 105, 6.9, 6.9, 3.6, 10.1, 252, '2024-11-05 07:40:00', 210, 'Breakfast', 9), +(287, 'Pasta', 531.9, 22.3, 4.1, 0.8, 105.6, 0, '2024-11-05 12:21:00', 406, 'Lunch', 9), +(288, 'Sweet potato', 169.7, 2.7, 0.2, 0, 37, 5, '2024-11-05 12:21:00', 168, 'Lunch', 9), +(289, 'Onion', 198.8, 7, 0.5, 0, 46.2, 14.9, '2024-11-05 19:11:00', 497, 'Dinner', 9), +(290, 'Avocado', 140.8, 1.8, 12.8, 1.8, 7.5, 6.2, '2024-11-05 19:11:00', 88, 'Dinner', 9), +(291, 'Lamb', 423.4, 25.2, 33.4, 13.2, 0, 0, '2024-11-06 07:39:00', 144, 'Breakfast', 9), +(292, 'Bread', 800.3, 28.4, 13.9, 3.9, 157, 453, '2024-11-06 13:54:00', 302, 'Lunch', 9), +(293, 'Carrot', 37.3, 0.8, 0.2, 0, 8.7, 62.8, '2024-11-06 13:54:00', 91, 'Lunch', 9), +(294, 'Broccoli', 49.3, 4.1, 0.9, 0.3, 9.6, 95.7, '2024-11-06 17:03:00', 145, 'Dinner', 9), +(295, 'Carrot', 123.4, 2.7, 0.6, 0, 28.9, 207.7, '2024-11-06 17:03:00', 301, 'Dinner', 9), +(296, 'Chicken thigh', 855, 117.2, 64.7, 22.6, 0, 0, '2024-11-07 08:12:00', 434, 'Breakfast', 9), +(297, 'Chicken breast', 585.8, 110, 21.7, 7.1, 0, 244.9, '2024-11-07 08:12:00', 355, 'Breakfast', 9), +(298, 'Salmon', 931.1, 99.4, 61.5, 17.2, 22.6, 0, '2024-11-07 12:45:00', 452, 'Lunch', 9), +(299, 'Bread', 556.5, 19.7, 9.7, 2.7, 109.2, 315, '2024-11-07 12:45:00', 210, 'Lunch', 9), +(300, 'Carrot', 96.8, 2.1, 0.5, 0, 22.7, 162.8, '2024-11-07 17:14:00', 236, 'Dinner', 9), +(301, 'Chicken thigh', 382.2, 52.4, 28.9, 10.1, 0, 0, '2024-11-08 06:20:00', 194, 'Breakfast', 9), +(302, 'Yogurt', 679.3, 23.9, 28, 15.6, 45.9, 573.8, '2024-11-08 06:20:00', 459, 'Breakfast', 9), +(303, 'Lettuce', 23.2, 2.3, 0.6, 0.3, 4.9, 14.5, '2024-11-08 13:27:00', 290, 'Lunch', 9), +(304, 'Lettuce', 28.4, 2.8, 0.7, 0.4, 6, 17.8, '2024-11-08 13:27:00', 355, 'Lunch', 9), +(305, 'Spinach', 77.7, 9.1, 1.4, 0.3, 12.2, 267, '2024-11-08 19:43:00', 338, 'Dinner', 9), +(306, 'Banana', 420, 5.2, 1.2, 0.4, 108, 4, '2024-11-09 06:39:00', 400, 'Breakfast', 9), +(307, 'Banana', 475.7, 5.9, 1.4, 0.5, 122.3, 4.5, '2024-11-09 12:24:00', 453, 'Lunch', 9), +(308, 'Beef', 1110, 97.7, 93.2, 35.5, 0, 222, '2024-11-09 17:23:00', 444, 'Dinner', 9), +(309, 'Lettuce', 36.2, 3.6, 0.9, 0.5, 7.7, 22.6, '2024-11-09 17:23:00', 452, 'Dinner', 9), +(310, 'Chicken thigh', 979.1, 134.2, 74.1, 25.8, 0, 0, '2024-11-10 07:24:00', 497, 'Breakfast', 9), +(311, 'Broccoli', 154.7, 12.7, 2.7, 0.9, 30, 300.3, '2024-11-10 07:24:00', 455, 'Breakfast', 9), +(312, 'Egg', 84.2, 6.8, 6, 1.7, 0.4, 67, '2024-11-10 12:42:00', 108, 'Lunch', 9), +(313, 'Pork', 903.8, 56.6, 73.9, 27.4, 0, 183.8, '2024-11-10 17:50:00', 375, 'Dinner', 9), +(314, 'Avocado', 348.8, 4.4, 31.8, 4.4, 18.5, 15.3, '2024-11-04 07:54:00', 218, 'Breakfast', 10), +(315, 'Spinach', 87.9, 10.3, 1.5, 0.4, 13.8, 301.8, '2024-11-04 07:54:00', 382, 'Breakfast', 10), +(316, 'Carrot', 203.4, 4.5, 1, 0, 47.6, 342.2, '2024-11-04 12:17:00', 496, 'Lunch', 10), +(317, 'Cucumber', 47.8, 2.1, 0.3, 0, 11.4, 6, '2024-11-04 12:17:00', 299, 'Lunch', 10), +(318, 'Yogurt', 217.6, 7.6, 9, 5, 14.7, 183.8, '2024-11-04 19:25:00', 147, 'Dinner', 10), +(319, 'Avocado', 281.6, 3.5, 25.7, 3.5, 15, 12.3, '2024-11-04 19:25:00', 176, 'Dinner', 10), +(320, 'Sweet potato', 168.7, 2.7, 0.2, 0, 36.7, 5, '2024-11-05 08:55:00', 167, 'Breakfast', 10), +(321, 'Milk', 187, 12.3, 12.3, 6.4, 18, 448.8, '2024-11-05 13:51:00', 374, 'Lunch', 10), +(322, 'Sweet potato', 95.9, 1.5, 0.1, 0, 20.9, 2.8, '2024-11-05 18:32:00', 95, 'Dinner', 10), +(323, 'Salmon', 883.7, 94.4, 58.3, 16.3, 21.4, 0, '2024-11-06 06:57:00', 429, 'Breakfast', 10), +(324, 'Yogurt', 125.8, 4.4, 5.2, 2.9, 8.5, 106.2, '2024-11-06 06:57:00', 85, 'Breakfast', 10), +(325, 'Cheese', 1949.7, 99.9, 160.5, 112.5, 6.3, 0, '2024-11-06 11:31:00', 485, 'Lunch', 10), +(326, 'Broccoli', 32, 2.6, 0.6, 0.2, 6.2, 62, '2024-11-06 11:31:00', 94, 'Lunch', 10), +(327, 'Sweet potato', 99, 1.6, 0.1, 0, 21.6, 2.9, '2024-11-06 18:01:00', 98, 'Dinner', 10), +(328, 'Broccoli', 133.3, 11, 2.4, 0.8, 25.9, 258.7, '2024-11-06 18:01:00', 392, 'Dinner', 10), +(329, 'Pasta', 615.7, 25.9, 4.7, 0.9, 122.2, 0, '2024-11-07 06:30:00', 470, 'Breakfast', 10), +(330, 'Banana', 113.4, 1.4, 0.3, 0.1, 29.2, 1.1, '2024-11-07 06:30:00', 108, 'Breakfast', 10), +(331, 'Bread', 429.3, 15.2, 7.5, 2.1, 84.2, 243, '2024-11-07 11:27:00', 162, 'Lunch', 10), +(332, 'Bread', 927.5, 32.9, 16.1, 4.5, 182, 525, '2024-11-07 11:27:00', 350, 'Lunch', 10), +(333, 'Brown rice', 995.8, 23.1, 13.8, 1.8, 207.5, 0, '2024-11-07 19:52:00', 461, 'Dinner', 10), +(334, 'Potato', 325.7, 8.5, 0.4, 0, 73.2, 42.3, '2024-11-08 06:05:00', 423, 'Breakfast', 10), +(335, 'Egg', 43.7, 3.5, 3.1, 0.9, 0.2, 34.7, '2024-11-08 12:47:00', 56, 'Lunch', 10), +(336, 'Potato', 139.4, 3.6, 0.2, 0, 31.3, 18.1, '2024-11-08 12:47:00', 181, 'Lunch', 10), +(337, 'Yogurt', 722.2, 25.4, 29.8, 16.6, 48.8, 610, '2024-11-08 19:02:00', 488, 'Dinner', 10), +(338, 'Egg', 373.6, 30.2, 26.8, 7.7, 1.9, 297, '2024-11-08 19:02:00', 479, 'Dinner', 10), +(339, 'Pasta', 313.1, 13.1, 2.4, 0.5, 62.1, 0, '2024-11-09 06:07:00', 239, 'Breakfast', 10), +(340, 'Egg', 213.7, 17.3, 15.3, 4.4, 1.1, 169.9, '2024-11-09 06:07:00', 274, 'Breakfast', 10), +(341, 'Garlic', 254.8, 12.2, 28, 6, 11.3, 303.9, '2024-11-09 11:31:00', 182, 'Lunch', 10), +(342, 'Sweet potato', 388.9, 6.2, 0.4, 0, 84.7, 11.6, '2024-11-09 11:31:00', 385, 'Lunch', 10), +(343, 'Banana', 158.6, 2, 0.5, 0.2, 40.8, 1.5, '2024-11-09 19:46:00', 151, 'Dinner', 10), +(344, 'Tomato', 15.7, 0.8, 0.2, 0, 3.4, 4.3, '2024-11-10 08:40:00', 87, 'Breakfast', 10), +(345, 'Carrot', 44.7, 1, 0.2, 0, 10.5, 75.2, '2024-11-10 12:45:00', 109, 'Lunch', 10), +(346, 'Sweet potato', 170.7, 2.7, 0.2, 0, 37.2, 5.1, '2024-11-10 19:16:00', 169, 'Dinner', 10), +(347, 'Egg', 624, 50.4, 44.8, 12.8, 3.2, 496, '2024-11-14 08:57:54', 800, 'Breakfast', 11), +(348, 'Spinach', 690, 81, 12, 3, 108, 2370, '2024-11-14 08:58:17', 3000, 'Dinner', 11), +(349, 'Chicken breast', 1650, 310, 61, 20, 10, 690, '2024-11-28 11:05:49', 1000, 'Breakfast', 1), +(350, 'Chicken breast', 1725.9, 324.26, 63.806, 20.92, 10.46, 721.74, '2024-11-28 15:04:46', 1046, 'Breakfast', 1); + +ALTER TABLE `food_items` + ADD PRIMARY KEY (`id`), + ADD KEY `user_id` (`user_id`); + + +ALTER TABLE `food_items` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=351; + +/* +ALTER TABLE `food_items` + ADD CONSTRAINT `food_items_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); + */ \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/08_alter_food_information.sql b/src/main/resources/db/changelog/changes/enson_change/08_alter_food_information.sql new file mode 100644 index 0000000..5103e00 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/08_alter_food_information.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`food_items` + add `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `createdBy` int DEFAULT NULL, + add `version` int NOT NULL DEFAULT '0', + add `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `modifiedBy` int DEFAULT NULL, + add `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/09_alter_food_information.sql b/src/main/resources/db/changelog/changes/enson_change/09_alter_food_information.sql new file mode 100644 index 0000000..4f616b8 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/09_alter_food_information.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +ALTER TABLE `food_items` +CHANGE COLUMN `kcal` `kcal` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `protein` `protein` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `fat` `fat` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `sat_fat` `sat_fat` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `carbo` `carbo` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `sodium` `sodium` DECIMAL(14,2) NULL DEFAULT NULL , +CHANGE COLUMN `size` `size` DECIMAL(14,2) NULL DEFAULT NULL ; diff --git a/src/main/resources/db/changelog/changes/enson_change/10_alter_food_items.sql b/src/main/resources/db/changelog/changes/enson_change/10_alter_food_items.sql new file mode 100644 index 0000000..b9bc01e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/10_alter_food_items.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql + +--changeset cyril:alter food items +--comment: alter food items + +ALTER TABLE `food_items` + CHANGE COLUMN `created` `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `id`, + CHANGE COLUMN `createdBy` `createdBy` INT NULL DEFAULT NULL AFTER `created`, + CHANGE COLUMN `version` `version` INT NOT NULL DEFAULT '0' AFTER `createdBy`, + CHANGE COLUMN `modified` `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `version`, + CHANGE COLUMN `modifiedBy` `modifiedBy` INT NULL DEFAULT NULL AFTER `modified`, + CHANGE COLUMN `deleted` `deleted` TINYINT(1) NOT NULL DEFAULT '0' AFTER `modifiedBy`; diff --git a/src/main/resources/db/changelog/changes/enson_change/11_create_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/11_create_user_information.sql new file mode 100644 index 0000000..7dfe625 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/11_create_user_information.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- Create table structure +CREATE TABLE `user_information` ( + `id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `gender` varchar(10) DEFAULT NULL, + `name` varchar(50) DEFAULT NULL, + `date_of_birth` date DEFAULT NULL, + `height` varchar(20) DEFAULT NULL, + `weight` varchar(20) DEFAULT NULL, + `allergy` varchar(50) DEFAULT NULL, + `goal` varchar(20) DEFAULT NULL, + `experience` varchar(20) DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/12_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/12_alter_user_information.sql new file mode 100644 index 0000000..cb8cba3 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/12_alter_user_information.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Alter table and insert data + +INSERT INTO `user_information` (`id`, `user_id`, `gender`, `name`, `date_of_birth`, `height`, `weight`, `allergy`, `goal`, `experience`, `created_at`) VALUES +(2, 1, 'Male', 'test', '2000-01-01', '175 cm', '61 kg', 'Peanuts', 'Muscle Gain', 'Expert', '2024-11-13 09:23:46'), +(3, 3, 'Male', 'm2', '2003-01-01', '162 cm', '80 kg', 'Egg', 'Muscle Gain', 'Intermediate', '2024-11-13 17:18:38'), +(4, 2, 'Male', 'm1', '2000-01-01', '162 cm', '53 kg', 'Fish', 'Muscle Gain', 'Expert', '2024-11-13 17:25:06'), +(5, 4, 'Male', 'm3', '2018-01-01', '164 cm', '125 lbs', 'Fish, Peanuts, Wheat', 'Muscle Gain', 'Intermediate', '2024-11-13 17:28:51'), +(6, 5, 'Male', 'm4', '2008-12-15', '169 cm', '108 kg', 'Peanuts, Fish', 'Nutrition', 'Beginner', '2024-11-13 17:32:53'), +(7, 6, 'Female', 'g1', '2015-01-01', '158 cm', '46 kg', 'Egg, Fish, Peanuts, Wheat, Soy, Milk', 'Muscle Gain', 'Expert', '2024-11-13 17:35:00'), +(8, 10, 'Female', 'g5', '1992-05-01', '213 cm', '140 kg', 'None', 'Muscle Gain', 'Beginner', '2024-11-13 17:37:00'), +(9, 9, 'Female', 'g4', '1989-01-01', '4 ft 11 in', '54 kg', 'Wheat', 'Nutrition', 'Beginner', '2024-11-13 17:37:25'), +(10, 8, 'Female', 'g3', '2006-06-03', '6 ft 4 in', '89 lbs', 'Egg', 'Diet', 'Intermediate', '2024-11-13 17:38:21'), +(11, 7, 'Female', 'g2', '2000-01-01', '170 cm', '60 kg', 'None', 'Diet', 'Beginner', '2024-11-13 17:38:46'), +(12, 11, 'Male', 'example', '2022-07-01', '171 cm', '54 kg', 'Peanuts, Wheat', 'Diet', 'Intermediate', '2024-11-14 08:58:59'); + +ALTER TABLE `user_information` + ADD PRIMARY KEY (`id`), + ADD KEY `user_id` (`user_id`); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/13_alter_information.sql b/src/main/resources/db/changelog/changes/enson_change/13_alter_information.sql new file mode 100644 index 0000000..9d1a2ad --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/13_alter_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user-table +--comment: Alter table + +ALTER TABLE `fhsmscdb`.`user` + ADD `has_information` TINYINT NOT NULL DEFAULT 0; diff --git a/src/main/resources/db/changelog/changes/enson_change/14_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/14_alter_user_information.sql new file mode 100644 index 0000000..22f7bd6 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/14_alter_user_information.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + add `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `createdBy` int DEFAULT NULL, + add `version` int NOT NULL DEFAULT '0', + add `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + add `modifiedBy` int DEFAULT NULL, + add `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/15_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/15_alter_user_information.sql new file mode 100644 index 0000000..decf572 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/15_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + DROP COLUMN `created_at`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/16_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/16_alter_user_information.sql new file mode 100644 index 0000000..2cf9807 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/16_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=27; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/17_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/17_alter_user_information.sql new file mode 100644 index 0000000..4dbcb5f --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/17_alter_user_information.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `age` int DEFAULT NULL, + add `activityLevel` varchar(20) DEFAULT NULL; diff --git a/src/main/resources/db/changelog/changes/enson_change/18_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/18_alter_user_information.sql new file mode 100644 index 0000000..de7a3d4 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/18_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY `activityLevel` varchar(20) DEFAULT NULL; diff --git a/src/main/resources/db/changelog/changes/enson_change/19_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/19_alter_user_information.sql new file mode 100644 index 0000000..100883a --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/19_alter_user_information.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:alter-user_information-table +--comment: Alter table + +Alter TABLE `fhsmscdb`.`user_information` + MODIFY COLUMN `age` int DEFAULT NULL AFTER `gender`, + MODIFY COLUMN `activityLevel` varchar(20) DEFAULT NULL AFTER `experience`; diff --git a/src/main/resources/db/changelog/changes/enson_change/20_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/20_alter_food1.sql new file mode 100644 index 0000000..540ca11 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/20_alter_food1.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Weight (g)` decimal(10,2) DEFAULT NULL, +ADD COLUMN `contains_egg` tinyint DEFAULT 3, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `contains_milk` tinyint DEFAULT 3, +ADD COLUMN `contains_wheat` tinyint DEFAULT 3, +ADD COLUMN `contains_shrimp` tinyint DEFAULT 3, +ADD COLUMN `contains_crab` tinyint DEFAULT 3, +ADD COLUMN `contains_buckwheat` tinyint DEFAULT 3, +ADD COLUMN `contains_peanut` tinyint DEFAULT 3, +ADD COLUMN `contains_walnut` tinyint DEFAULT 3; + +-- 重命名现有列以添加单位 +ALTER TABLE `fhsmscdb`.`food1` +CHANGE COLUMN `kcal` `Calories (kcal)` decimal(10,2), +CHANGE COLUMN `protein` `Protein (g)` decimal(10,2), +CHANGE COLUMN `fat` `Fat (g)` decimal(10,2), +DROP COLUMN `satFat`, +CHANGE COLUMN `carbo` `Carbohydrate (g)` decimal(10,2), +CHANGE COLUMN `sodium` `Sodium (mg)` decimal(10,2); + diff --git a/src/main/resources/db/changelog/changes/enson_change/21_alter_food_items.sql b/src/main/resources/db/changelog/changes/enson_change/21_alter_food_items.sql new file mode 100644 index 0000000..5c9388f --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/21_alter_food_items.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + + + +-- 重命名现有列以添加单位 +ALTER TABLE `fhsmscdb`.`food_items` +DROP COLUMN `sat_fat`; + + diff --git a/src/main/resources/db/changelog/changes/enson_change/22_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/22_alter_food1.sql new file mode 100644 index 0000000..3352387 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/22_alter_food1.sql @@ -0,0 +1,16 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Postassium (mg)` decimal(10,2) DEFAULT 0, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `Calcium (mg)`decimal(10,2) DEFAULT 0, +ADD COLUMN `Phosphorus (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Iron (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin A (μg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin B1 (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin B2 (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Niacin (mg)` decimal(10,2) DEFAULT 0, +ADD COLUMN `Vitamin C (mg)` decimal(10,2) DEFAULT 0; + diff --git a/src/main/resources/db/changelog/changes/enson_change/23_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/23_alter_food1.sql new file mode 100644 index 0000000..94c15e5 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/23_alter_food1.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:alter-food1-table +--comment: Alter table + +-- 添加新列 +Alter TABLE `fhsmscdb`.`food1` +DROP COLUMN `Ingredients`; +ALTER TABLE `fhsmscdb`.`food1` +MODIFY COLUMN `Postassium (mg)` decimal(10,2) DEFAULT 0 AFTER `Sodium (mg)`, +MODIFY COLUMN `Calcium (mg)` decimal(10,2) DEFAULT 0 AFTER `Postassium (mg)`, +MODIFY COLUMN `Phosphorus (mg)` decimal(10,2) DEFAULT 0 AFTER `Calcium (mg)`, +MODIFY COLUMN `Iron (mg)` decimal(10,2) DEFAULT 0 AFTER `Phosphorus (mg)`, +MODIFY COLUMN `Vitamin A (μg)` decimal(10,2) DEFAULT 0 AFTER `Iron (mg)`, +MODIFY COLUMN `Vitamin B1 (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin A (μg)`, +MODIFY COLUMN `Vitamin B2 (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin B1 (mg)`, +MODIFY COLUMN `Niacin (mg)` decimal(10,2) DEFAULT 0 AFTER `Vitamin B2 (mg)`, +MODIFY COLUMN `Vitamin C (mg)` decimal(10,2)DEFAULT 0 AFTER `Niacin (mg)`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/24_alter_food_items.sql b/src/main/resources/db/changelog/changes/enson_change/24_alter_food_items.sql new file mode 100644 index 0000000..6dad3d5 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/24_alter_food_items.sql @@ -0,0 +1,27 @@ +--liquibase formatted sql +--changeset terence:alter-food_items-table +--comment: Alter table + +-- 添加新列 +/* +Alter TABLE `fhsmscdb`.`food_items` +ADD COLUMN `potassium` decimal(10,2) DEFAULT 0, -- 0:●(使用) 1:○(油) 2:▲(接触) 3:×(未使用) +ADD COLUMN `calcium`decimal(10,2) DEFAULT 0, +ADD COLUMN `phosphorus` decimal(10,2) DEFAULT 0, +ADD COLUMN `iron` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminA` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminB1` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminB2` decimal(10,2) DEFAULT 0, +ADD COLUMN `niacin` decimal(10,2) DEFAULT 0, +ADD COLUMN `vitaminC` decimal(10,2) DEFAULT 0; +*/ +ALTER TABLE `fhsmscdb`.`food_items` +MODIFY COLUMN `potassium` decimal(10,2) DEFAULT 0 AFTER `sodium`, +MODIFY COLUMN `calcium` decimal(10,2) DEFAULT 0 AFTER `potassium`, +MODIFY COLUMN `phosphorus` decimal(10,2) DEFAULT 0 AFTER `calcium`, +MODIFY COLUMN `iron` decimal(10,2) DEFAULT 0 AFTER `phosphorus`, +MODIFY COLUMN `vitaminA` decimal(10,2) DEFAULT 0 AFTER `iron`, +MODIFY COLUMN `vitaminB1` decimal(10,2) DEFAULT 0 AFTER `vitaminA`, +MODIFY COLUMN `vitaminB2` decimal(10,2) DEFAULT 0 AFTER `vitaminB1`, +MODIFY COLUMN `niacin` decimal(10,2) DEFAULT 0 AFTER `vitaminB2`, +MODIFY COLUMN `vitaminC` decimal(10,2)DEFAULT 0 AFTER `niacin`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/25_create_Nutrients.sql b/src/main/resources/db/changelog/changes/enson_change/25_create_Nutrients.sql new file mode 100644 index 0000000..5a8f65e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/25_create_Nutrients.sql @@ -0,0 +1,40 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Nutrients` ( + `nutrient_id` int NOT NULL AUTO_INCREMENT, + `nutrient_name` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`nutrient_id`), + UNIQUE KEY `idx_nutrient_name` (`nutrient_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `Nutrient_Impacts` ( + `impact_id` int NOT NULL AUTO_INCREMENT, + `nutrient_id` int NOT NULL, + `organ_affected` varchar(100) NOT NULL, + `impact_type` ENUM('Positive', 'Negative') NOT NULL, + `impact_description` text NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`impact_id`), + FOREIGN KEY (`nutrient_id`) REFERENCES `Nutrients`(`nutrient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入基础营养素数据 +INSERT INTO `Nutrients` (nutrient_name) VALUES +('Calories'), +('Protein'), +('Fat'), +('Carbohydrate'), +('Sodium'), +('Potassium'), +('Calcium'), +('Phosphorus'), +('Iron'), +('Vitamin A'), +('Vitamin B1'), +('Vitamin B2'), +('Niacin'), +('Vitamin C'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql b/src/main/resources/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql new file mode 100644 index 0000000..1d3d939 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/26_alter_Nutrients_impacts.sql @@ -0,0 +1,26 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +INSERT INTO `Nutrient_Impacts` +(nutrient_id, organ_affected, impact_type, impact_description) +VALUES +(2, 'Muscles', 'Positive', 'Essential for muscle growth and repair'), +(2, 'Bones', 'Positive', 'Helps maintain bone mass'), +(2, 'Immune System', 'Positive', 'Supports immune system function'), + +-- Vitamin C impacts (假设 nutrient_id = 14) +(14, 'Immune System', 'Positive', 'Strengthens immune system and fights infections'), +(14, 'Skin', 'Positive', 'Promotes collagen production for healthy skin'), +(14, 'Blood Vessels', 'Positive', 'Supports blood vessel health'), + +-- Iron impacts (假设 nutrient_id = 9) +(9, 'Blood', 'Positive', 'Essential for red blood cell production'), +(9, 'Brain', 'Positive', 'Supports cognitive function'), +(9, 'Immune System', 'Positive', 'Helps maintain immune system'), + +-- Calcium impacts (假设 nutrient_id = 7) +(7, 'Bones', 'Positive', 'Essential for bone strength and density'), +(7, 'Teeth', 'Positive', 'Maintains dental health'), +(7, 'Muscles', 'Positive', 'Required for proper muscle function'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/27_create_Allergy.sql b/src/main/resources/db/changelog/changes/enson_change/27_create_Allergy.sql new file mode 100644 index 0000000..9158c2f --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/27_create_Allergy.sql @@ -0,0 +1,49 @@ +/* +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Allergies` ( + `allergy_id` int NOT NULL AUTO_INCREMENT, + `allergy_name` varchar(50) NOT NULL, + `description` text, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`allergy_id`), + UNIQUE KEY `idx_allergy_name` (`allergy_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 用户过敏记录表 +CREATE TABLE `User_Allergies` ( + `user_allergy_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `allergy_id` int NOT NULL, + `diagnosed_date` date, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_allergy_id`), + UNIQUE KEY `idx_user_allergy` (`user_id`, `allergy_id`), + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), + FOREIGN KEY (`allergy_id`) REFERENCES `Allergies`(`allergy_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入常见过敏原数据 +INSERT INTO `Allergies` (allergy_name, description) VALUES +('Peanuts', 'Common legume that can cause severe allergic reactions'), +('Tree Nuts', 'Including almonds, walnuts, cashews, etc.'), +('Milk', 'Dairy products and lactose'), +('Eggs', 'Both egg whites and yolks'), +('Soy', 'Soybeans and soy-based products'), +('Wheat', 'Including gluten sensitivity'), +('Fish', 'Various types of fish'), +('Shellfish', 'Crustaceans and mollusks'), +('Sesame', 'Seeds and sesame oil'), +('Sulfites', 'Common food preservative'); + +-- 插入示例用户过敏数据 +INSERT INTO `User_Allergies` +(user_id, allergy_id, severity_level, symptoms, diagnosed_date) +VALUES +(1, 1, 'Severe', 'Anaphylaxis, difficulty breathing, swelling', '2023-01-15'), +(1, 3, 'Moderate', 'Digestive issues, skin rash', '2023-02-20'), +(2, 4, 'Mild', 'Mild skin irritation', '2023-03-10'), +(2, 8, 'Severe', 'Severe rash, breathing difficulties', '2023-04-05'); +*/ \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/28_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/28_alter_food1.sql new file mode 100644 index 0000000..54c725a --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/28_alter_food1.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +Alter TABLE `fhsmscdb`.`food1` +ADD COLUMN `Restaurant_ID` int(11) DEFAULT NULL; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/29_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/29_alter_food1.sql new file mode 100644 index 0000000..4f03ee5 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/29_alter_food1.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + + +INSERT INTO `food1` (`name`, `Calories (kcal)`, `Protein (g)`, `Fat (g)`, `Carbohydrate (g)`, `Sodium (mg)`, `Postassium (mg)`, `Calcium (mg)`, `Phosphorus (mg)`,`Iron (mg)`,`Vitamin A (μg)`,`Vitamin B1 (mg)`,`Vitamin B2 (mg)`,`Niacin (mg)`,`Vitamin C (mg)`,`food_type`,`Weight (g)`,`contains_egg`, `contains_milk`, `contains_wheat`, `contains_shrimp`,`contains_crab`,`contains_buckwheat`,`contains_peanut`,`contains_walnut`,`Restaurant_ID`) VALUES +('N.Y. Barbecue Sausage & Cheese McMuffin',504,16.6,32.9,35.1,952,263,162,190,1.1,60,0.2,0.21,6,3,'food',152,0,0,0,3,3,3,3,3,1); diff --git a/src/main/resources/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql b/src/main/resources/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql new file mode 100644 index 0000000..6af4d2e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/30_create_Nutrients_Need.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `Nutrients_Need` ( + `id` int NOT NULL, + `bmr` DECIMAL(10,2) NOT NULL, + `tdee` DECIMAL(10,2) NOT NULL, + `protein_need` DECIMAL(10,2) NOT NULL, + `fat_need` DECIMAL(10,2) NOT NULL, + `carbo_need` DECIMAL(10,2) NOT NULL, + `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + FOREIGN KEY (`id`) REFERENCES `user` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/src/main/resources/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql b/src/main/resources/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql new file mode 100644 index 0000000..2c04e59 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/31_create_Nutrients_Need_update.sql @@ -0,0 +1,64 @@ +--liquibase formatted sql +--changeset terence:create-update-trigger splitStatements:false +--comment: Create update trigger + +CREATE TRIGGER update_user_Nutrients_Need +AFTER UPDATE ON user_information +FOR EACH ROW +BEGIN + DECLARE calculated_bmr DECIMAL(10,2); + DECLARE calculated_tdee DECIMAL(10,2); + DECLARE user_age INT; + DECLARE weight_kg DECIMAL(5,2); + DECLARE height_cm DECIMAL(5,2); + + -- Extract numeric values from weight and height + SET weight_kg = CAST(REPLACE(NEW.weight, ' kg', '') AS DECIMAL(5,2)); + SET height_cm = CAST(REPLACE(NEW.height, ' cm', '') AS DECIMAL(5,2)); + + -- Calculate age + SET user_age = FLOOR(DATEDIFF(CURRENT_DATE, FROM_UNIXTIME(NEW.date_of_birth/1000)) / 365); + + -- Calculate BMR based on gender + IF NEW.gender = 'Male' THEN + SET calculated_bmr = 88.362 + (13.397 * weight_kg) + (4.799 * height_cm) - (5.677 * user_age); + ELSE + SET calculated_bmr = 447.593 + (9.247 * weight_kg) + (3.098 * height_cm) - (4.330 * user_age); + END IF; + + -- Calculate TDEE based on activity level + SET calculated_tdee = calculated_bmr * + CASE NEW.activitylevel + WHEN 'Sedentary' THEN 1.2 + WHEN '1-2 times' THEN 1.375 + WHEN '3-5 times' THEN 1.55 + WHEN '6-7 times' THEN 1.725 + WHEN 'Professional' THEN 1.9 + ELSE 1.2 + END; + + -- Insert or update TDEE record + INSERT INTO User_TDEE ( + user_id, + bmr, + tdee, + protein_need, + fat_need, + carbo_need + ) + VALUES ( + NEW.user_id, + calculated_bmr, + calculated_tdee, + weight_kg * 2, + (calculated_tdee * 0.25) / 9, + (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4 + ) + ON DUPLICATE KEY UPDATE + bmr = calculated_bmr, + tdee = calculated_tdee, + protein_need = weight_kg * 2, + fat_need = (calculated_tdee * 0.25) / 9, + carbo_need = (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4, + last_updated = CURRENT_TIMESTAMP; +END; diff --git a/src/main/resources/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql b/src/main/resources/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql new file mode 100644 index 0000000..946d78a --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/32_create_Nutrients_Need_update.sql @@ -0,0 +1,64 @@ +--liquibase formatted sql +--changeset terence:create-update-trigger splitStatements:false +--comment: Create update trigger + +CREATE TRIGGER update_user_Nutrients_Need +AFTER UPDATE ON user_information +FOR EACH ROW +BEGIN + DECLARE calculated_bmr DECIMAL(10,2); + DECLARE calculated_tdee DECIMAL(10,2); + DECLARE user_age INT; + DECLARE weight_kg DECIMAL(5,2); + DECLARE height_cm DECIMAL(5,2); + + -- Extract numeric values from weight and height + SET weight_kg = CAST(REPLACE(NEW.weight, ' kg', '') AS DECIMAL(5,2)); + SET height_cm = CAST(REPLACE(NEW.height, ' cm', '') AS DECIMAL(5,2)); + + -- Calculate age + SET user_age = FLOOR(DATEDIFF(CURRENT_DATE, FROM_UNIXTIME(NEW.date_of_birth/1000)) / 365); + + -- Calculate BMR based on gender + IF NEW.gender = 'Male' THEN + SET calculated_bmr = 88.362 + (13.397 * weight_kg) + (4.799 * height_cm) - (5.677 * user_age); + ELSE + SET calculated_bmr = 447.593 + (9.247 * weight_kg) + (3.098 * height_cm) - (4.330 * user_age); + END IF; + + -- Calculate TDEE based on activity level + SET calculated_tdee = calculated_bmr * + CASE NEW.activitylevel + WHEN 'Sedentary' THEN 1.2 + WHEN '1-2 times' THEN 1.375 + WHEN '3-5 times' THEN 1.55 + WHEN '6-7 times' THEN 1.725 + WHEN 'Professional' THEN 1.9 + ELSE 1.2 + END; + + -- Insert or update TDEE record + INSERT INTO nutrients_need ( + user_id, + bmr, + tdee, + protein_need, + fat_need, + carbo_need + ) + VALUES ( + NEW.user_id, + calculated_bmr, + calculated_tdee, + weight_kg * 2, + (calculated_tdee * 0.25) / 9, + (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4 + ) + ON DUPLICATE KEY UPDATE + bmr = calculated_bmr, + tdee = calculated_tdee, + protein_need = weight_kg * 2, + fat_need = (calculated_tdee * 0.25) / 9, + carbo_need = (calculated_tdee - (weight_kg * 2 * 4) - (calculated_tdee * 0.25)) / 4, + last_updated = CURRENT_TIMESTAMP; +END; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/33_create_Allergy.sql b/src/main/resources/db/changelog/changes/enson_change/33_create_Allergy.sql new file mode 100644 index 0000000..cf83fa7 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/33_create_Allergy.sql @@ -0,0 +1,44 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +CREATE TABLE `Allergies` ( + `allergy_id` int NOT NULL AUTO_INCREMENT, + `allergy_name` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`allergy_id`), + UNIQUE KEY `idx_allergy_name` (`allergy_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +*/ +CREATE TABLE `User_Allergies` ( + `user_allergy_id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `allergy_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`user_allergy_id`), + UNIQUE KEY `idx_user_allergy` (`user_id`, `allergy_id`), + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), + FOREIGN KEY (`allergy_id`) REFERENCES `Allergies`(`allergy_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 插入常见过敏原数据 +INSERT INTO `Allergies` (allergy_name) VALUES +('Peanuts'), +('Tree Nuts'), +('Milk'), +('Eggs'), +('Soy' ), +('Wheat'), +('Fish'), +('Shellfish'), +('Sesame'), +('Sulfites'); + +-- 插入示例用户过敏数据 +INSERT INTO `User_Allergies` +(user_id, allergy_id ) +VALUES +(1, 1), +(1, 3), +(2, 4), +(2, 8); diff --git a/src/main/resources/db/changelog/changes/enson_change/34_alter_food1.sql b/src/main/resources/db/changelog/changes/enson_change/34_alter_food1.sql new file mode 100644 index 0000000..1af82b9 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/34_alter_food1.sql @@ -0,0 +1,19 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +Alter TABLE `fhsmscdb`.`food1` + CHANGE `Calories (kcal)` Calories decimal(10,2), + CHANGE `Protein (g)` Protein decimal(10,2), + CHANGE `Fat (g)` Fat decimal(10,2), + CHANGE `Carbohydrate (g)` Carbohydrate decimal(10,2), + CHANGE `Sodium (mg)` Sodium decimal(10,2), + CHANGE `Postassium (mg)` Potassium decimal(10,2), + CHANGE `Calcium (mg)` Calcium decimal(10,2), + CHANGE `Phosphorus (mg)` Phosphorus decimal(10,2), + CHANGE `Iron (mg)` Iron decimal(10,2), + CHANGE `Vitamin A (μg)` VitaminA decimal(10,2), + CHANGE `Vitamin B1 (mg)` VitaminB1 decimal(10,2), + CHANGE `Vitamin B2 (mg)` VitaminB2 decimal(10,2), + CHANGE `Niacin (mg)` Niacin decimal(10,2), + CHANGE `Vitamin C (mg)` VitaminC decimal(10,2), + CHANGE `Weight (g)` Weight decimal(10,2); diff --git a/src/main/resources/db/changelog/changes/enson_change/35_alter_user_information.sql b/src/main/resources/db/changelog/changes/enson_change/35_alter_user_information.sql new file mode 100644 index 0000000..b0069cf --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/35_alter_user_information.sql @@ -0,0 +1,6 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +Alter TABLE `fhsmscdb`.`user_information` + drop column `allergy`; + diff --git a/src/main/resources/db/changelog/changes/enson_change/36_create_restaurant_information.sql b/src/main/resources/db/changelog/changes/enson_change/36_create_restaurant_information.sql new file mode 100644 index 0000000..5a23e72 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/36_create_restaurant_information.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `FB_Operator` ( + + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_name` varchar(50) NOT NULL, + `FB_type` varchar(255) NOT NULL, + `FB_cuisine` varchar(20) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`) +)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/src/main/resources/db/changelog/changes/enson_change/37_alter_restaurant_information.sql b/src/main/resources/db/changelog/changes/enson_change/37_alter_restaurant_information.sql new file mode 100644 index 0000000..a46cb1b --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/37_alter_restaurant_information.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +INSERT INTO `FB_Operator` +(FB_id, FB_name, FB_type, FB_cuisine) +VALUES +(1, 'KFC', 'Fast Food', 'Fast Food'), +(2, 'McDonald', 'Fast Food', 'Fast Food'), +(3, '大家樂', 'Cha Chaan Teng', 'Hong Kong Style'), +(4, '翠華餐廳', 'Cha Chaan Teng', 'Hong Kong Style'), +(5, '美心', 'Chinese Restaurant', 'Cantonese'), +(6, '點點心', 'Dim Sum', 'Cantonese'), +(7, '吉野家', 'Fast Food', 'Japanese'), +(8, '壽司郎', 'Japanese Restaurant', 'Japanese'), +(9, '譚仔雲南米線', 'Noodle Shop', 'Chinese'), +(10, '大快活', 'Fast Food', 'Hong Kong Style') diff --git a/src/main/resources/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql new file mode 100644 index 0000000..c1f612d --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/38_create_restaurant_informatio.sql @@ -0,0 +1,14 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +CREATE TABLE `FB_Outlets` ( + + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_outlet_id` int NOT NULL, + `FB_outlet_district` varchar(255) NOT NULL, + `FB_outlet_address` varchar(255) NOT NULL, + `FB_outlet_phone` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`) +)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/src/main/resources/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql new file mode 100644 index 0000000..40b5021 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/39_alter_restaurant_informatio.sql @@ -0,0 +1,14 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_id` int NOT NULL; +ALTER TABLE `FB_Outlets` DROP PRIMARY KEY; + +ALTER TABLE `FB_Outlets` ADD PRIMARY KEY (`FB_outlet_id`); + + +ALTER TABLE `FB_Outlets` ADD CONSTRAINT `fk_fb_outlets_operator` +FOREIGN KEY (`FB_id`) REFERENCES `fb_operator`(`FB_id`); + + +ALTER TABLE `FB_Outlets` ADD INDEX `idx_fb_id` (`FB_id`); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql new file mode 100644 index 0000000..966b425 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/40_alter_restaurant_informatio.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_outlet_id` int NOT NULL FIRST; + + +ALTER TABLE `FB_Outlets` MODIFY COLUMN `FB_id` int NOT NULL AFTER `FB_outlet_id`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql new file mode 100644 index 0000000..2d17218 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/41_alter_restaurant_informatio.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +DROP TABLE `FB_Outlets`; + +-- 用新结构创建表 +CREATE TABLE `FB_Outlets` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_outlet_id` int NOT NULL, + `FB_id` int NOT NULL, + `FB_outlet_district` varchar(255), + `FB_outlet_address` varchar(255), + `FB_outlet_phone` varchar(255), + `created_at` timestamp, + PRIMARY KEY (`id`), + INDEX `idx_fb_outlet_id` (`FB_outlet_id`), + INDEX `idx_fb_id` (`FB_id`), + CONSTRAINT `fk_fb_outlets_operator` FOREIGN KEY (`FB_id`) + REFERENCES `fb_operator` (`FB_id`) +); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql new file mode 100644 index 0000000..364fe21 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/42_alter_restaurant_informatio.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +INSERT INTO `FB_Outlets` (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(201, 2, '金鐘', '夏愨道18號海富中心地下1-41號舖A部份', '2520 1329', CURRENT_TIMESTAMP), +(202, 2, '灣仔', '軒尼詩道302-308號集成中心Shop G11-17 ,G/F', '2893 9503', CURRENT_TIMESTAMP); + + +INSERT INTO FB_Outlets (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(301, 3, '銅鑼灣', '波斯富街48號軒尼詩大廈3樓', '2808 0139', CURRENT_TIMESTAMP), +(302, 3, '銅鑼灣', '地下英皇道6-8 號香港天后百利大廈', '2807 3864', CURRENT_TIMESTAMP); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql b/src/main/resources/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql new file mode 100644 index 0000000..76c796d --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/43_alter_restaurant_informatio.sql @@ -0,0 +1,8 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +INSERT INTO `fb_outlets` (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) +VALUES +(201, 2, '金鐘', '夏愨道18號海富中心地下1-41號舖A部份', '2520 1329', CURRENT_TIMESTAMP), +(202, 2, '灣仔', '軒尼詩道302-308號集成中心Shop G11-17 ,G/F', '2893 9503', CURRENT_TIMESTAMP); + diff --git a/src/main/resources/db/changelog/changes/enson_change/44_create_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/44_create_muti_table.sql new file mode 100644 index 0000000..93df43d --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/44_create_muti_table.sql @@ -0,0 +1,71 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +-- 餐厅菜单表 +CREATE TABLE `FB_Menu` ( + `FB_id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Menu_Item_Description` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_id`), + UNIQUE KEY `UK_FB_menu_id` (`FB_menu_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 菜单项成分表 +CREATE TABLE `FB_Menu_Item_Ingredient` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Ingredient_id` int NOT NULL, + `Ingredient_Volume` decimal(10,2) NOT NULL, + `Ingredient_Volume_Unit` varchar(50) NOT NULL, + `Allergy_Substance_ID` int, + `Nutrient_ID` int, + `Nutrient_Volume` decimal(10,2), + `Nutrient_Volume_Unit` varchar(50), + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 菜单与门店关系表 +CREATE TABLE `FB_Menu_and_Outlet_Relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `FB_outlet_id` int NOT NULL, + `FB_menu_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_rel_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_rel_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 每日营养摄入汇总表 +CREATE TABLE `daily_nutrition_summary` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `date` date NOT NULL, + `total_calories` decimal(10,2) DEFAULT 0, + `total_protein` decimal(10,2) DEFAULT 0, + `total_fat` decimal(10,2) DEFAULT 0, + `total_carbs` decimal(10,2) DEFAULT 0, + `total_sodium` decimal(10,2) DEFAULT 0, + `total_potassium` decimal(10,2) DEFAULT 0, + `total_calcium` decimal(10,2) DEFAULT 0, + `total_phosphorus` decimal(10,2) DEFAULT 0, + `total_iron` decimal(10,2) DEFAULT 0, + `total_vitamin_a` decimal(10,2) DEFAULT 0, + `total_vitamin_b1` decimal(10,2) DEFAULT 0, + `total_vitamin_b2` decimal(10,2) DEFAULT 0, + `total_niacin` decimal(10,2) DEFAULT 0, + `total_vitamin_c` decimal(10,2) DEFAULT 0, + `breakfast_calories` decimal(10,2) DEFAULT 0, + `lunch_calories` decimal(10,2) DEFAULT 0, + `dinner_calories` decimal(10,2) DEFAULT 0, + `snack_calories` decimal(10,2) DEFAULT 0, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_user_date` (`user_id`, `date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/45_alter_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/45_alter_muti_table.sql new file mode 100644 index 0000000..7cb0ec1 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/45_alter_muti_table.sql @@ -0,0 +1,43 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +CREATE TABLE `FB_Menu_Item_Nutrition` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Calories` decimal(10,2) DEFAULT 0, + `Protein` decimal(10,2) DEFAULT 0, + `Fat` decimal(10,2) DEFAULT 0, + `Carbohydrate` decimal(10,2) DEFAULT 0, + `Sodium` decimal(10,2) DEFAULT 0, + `Potassium` decimal(10,2) DEFAULT 0, + `Calcium` decimal(10,2) DEFAULT 0, + `Phosphorus` decimal(10,2) DEFAULT 0, + `Iron` decimal(10,2) DEFAULT 0, + `VitaminA` decimal(10,2) DEFAULT 0, + `VitaminB1` decimal(10,2) DEFAULT 0, + `VitaminB2` decimal(10,2) DEFAULT 0, + `Niacin` decimal(10,2) DEFAULT 0, + `VitaminC` decimal(10,2) DEFAULT 0, + `Nutrient_Volume` decimal(10,2) DEFAULT 0, + `Nutrient_Volume_Unit` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `UK_FB_menu_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_nutrition_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `FB_Menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +*/ +CREATE TABLE `Ingredient` ( + `Ingredient_id` int NOT NULL AUTO_INCREMENT, + `Ingredient_Name` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`Ingredient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +ALTER TABLE `fhsmscdb`.`fb_menu_item_ingredient` + DROP COLUMN `Nutrient_ID`, + DROP COLUMN `Nutrient_Volume`, + DROP COLUMN `Nutrient_Volume_Unit`; + + + diff --git a/src/main/resources/db/changelog/changes/enson_change/46_alter_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/46_alter_muti_table.sql new file mode 100644 index 0000000..44e0013 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/46_alter_muti_table.sql @@ -0,0 +1,175 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data + +DROP TABLE IF EXISTS fb_menu_and_outlet_relationship; +DROP TABLE IF EXISTS fb_menu_item_ingredient; +DROP TABLE IF EXISTS fb_menu_item_nutrition; +DROP TABLE IF EXISTS fb_menu; +DROP TABLE IF EXISTS ingredient; + + +CREATE TABLE `ingredient` ( + `Ingredient_id` int NOT NULL AUTO_INCREMENT, + `Ingredient_Name` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`Ingredient_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- 重新创建fb_menu表,使用FB_menu_id作为主键 +CREATE TABLE `fb_menu` ( + `FB_id` int NOT NULL, + `FB_menu_id` int NOT NULL AUTO_INCREMENT, + `Menu_Item_Description` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`FB_menu_id`), + INDEX `idx_FB_id` (`FB_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `fb_menu_item_nutrition` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Calories` decimal(10,2) DEFAULT 0, + `Protein` decimal(10,2) DEFAULT 0, + `Fat` decimal(10,2) DEFAULT 0, + `Carbohydrate` decimal(10,2) DEFAULT 0, + `Sodium` decimal(10,2) DEFAULT 0, + `Potassium` decimal(10,2) DEFAULT 0, + `Calcium` decimal(10,2) DEFAULT 0, + `Phosphorus` decimal(10,2) DEFAULT 0, + `Iron` decimal(10,2) DEFAULT 0, + `VitaminA` decimal(10,2) DEFAULT 0, + `VitaminB1` decimal(10,2) DEFAULT 0, + `VitaminB2` decimal(10,2) DEFAULT 0, + `Niacin` decimal(10,2) DEFAULT 0, + `VitaminC` decimal(10,2) DEFAULT 0, + `Nutrient_Volume` decimal(10,2) DEFAULT 0, + `Nutrient_Volume_Unit` varchar(50) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_nutrition_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_nutrition_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + + +CREATE TABLE `fb_menu_item_ingredient` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_menu_id` int NOT NULL, + `Ingredient_id` int NOT NULL, + `Ingredient_Volume` decimal(10,2) NOT NULL, + `Ingredient_Volume_Unit` varchar(50) NOT NULL, + `Allergy_Substance_ID` int, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_id` (`FB_menu_id`), + KEY `FK_Ingredient_id` (`Ingredient_id`), + CONSTRAINT `FK_FB_menu_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE, + CONSTRAINT `FK_Ingredient_id` FOREIGN KEY (`Ingredient_id`) REFERENCES `ingredient` (`Ingredient_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `fb_menu_and_outlet_relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `FB_outlet_id` int NOT NULL, + `FB_menu_id` int NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `FK_FB_menu_rel_id` (`FB_menu_id`), + CONSTRAINT `FK_FB_menu_rel_id` FOREIGN KEY (`FB_menu_id`) REFERENCES `fb_menu` (`FB_menu_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO fb_menu (FB_id, FB_menu_id, Menu_Item_Description, created_at) VALUES +(1, 101, '香煎三文鱼配时蔬', NOW()), +(1, 102, '黑椒牛排配蘑菇酱', NOW()), +(1, 103, '泰式青咖喱鸡', NOW()), +(1, 104, '意式肉酱面配帕玛森芝士', NOW()), +(1, 105, '地中海风味沙拉', NOW()), +(2, 201, '清蒸鲈鱼配姜葱', NOW()), +(2, 202, '红烧狮子头', NOW()), +(2, 203, '宫保鸡丁配花生米', NOW()); + +-- 插入营养信息数据 +INSERT INTO fb_menu_item_nutrition (FB_menu_id, Calories, Protein, Fat, Carbohydrate, Sodium, Potassium, Calcium, Phosphorus, Iron, VitaminA, VitaminB1, VitaminB2, Niacin, VitaminC, Nutrient_Volume, Nutrient_Volume_Unit, created_at) VALUES + +(101, 275.00, 25.00, 15.00, 10.00, 150.00, 450.00, 30.00, 250.00, 1.80, 120.00, 0.15, 0.20, 5.00, 15.00, 350.00, 'g', NOW()), + + +(102, 320.00, 30.00, 20.00, 5.00, 200.00, 500.00, 20.00, 200.00, 3.50, 50.00, 0.10, 0.15, 7.00, 5.00, 300.00, 'g', NOW()), + + +(103, 310.00, 22.00, 18.00, 15.00, 300.00, 400.00, 50.00, 180.00, 2.00, 80.00, 0.12, 0.18, 6.00, 20.00, 350.00, 'g', NOW()), + + +(104, 480.00, 20.00, 15.00, 65.00, 800.00, 350.00, 150.00, 200.00, 2.50, 100.00, 0.20, 0.25, 4.00, 10.00, 400.00, 'g', NOW()), + + +(105, 200.00, 8.00, 12.00, 15.00, 120.00, 300.00, 100.00, 120.00, 1.20, 150.00, 0.08, 0.12, 2.00, 45.00, 250.00, 'g', NOW()); + + +INSERT INTO ingredient (Ingredient_id, Ingredient_Name, created_at) VALUES +(1, '三文鱼', NOW()), +(2, '橄榄油', NOW()), +(3, '西兰花', NOW()), +(4, '胡萝卜', NOW()), +(5, '牛排', NOW()), +(6, '黑胡椒', NOW()), +(7, '蘑菇', NOW()), +(8, '鸡胸肉', NOW()), +(9, '青咖喱酱', NOW()), +(10, '椰奶', NOW()), +(11, '意大利面', NOW()), +(12, '番茄', NOW()), +(13, '牛肉碎', NOW()), +(14, '帕玛森芝士', NOW()), +(15, '生菜', NOW()), +(16, '黄瓜', NOW()), +(17, '橄榄', NOW()), +(18, '羊奶芝士', NOW()); + + +INSERT INTO fb_menu_item_ingredient (FB_menu_id, Ingredient_id, Ingredient_Volume, Ingredient_Volume_Unit, Allergy_Substance_ID, created_at) VALUES + +(101, 1, 200.00, 'g', 7, NOW()), -- 三文鱼 (鱼类过敏 +(101, 2, 15.00, 'ml', NULL, NOW()), +(101, 3, 100.00, 'g', NULL, NOW()), +(101, 4, 50.00, 'g', NULL, NOW()), + + +(102, 5, 250.00, 'g', NULL, NOW()), +(102, 6, 5.00, 'g', NULL, NOW()), +(102, 7, 100.00, 'g', NULL, NOW()), +(102, 2, 10.00, 'ml', NULL, NOW()), + + +(103, 8, 180.00, 'g', NULL, NOW()), +(103, 9, 30.00, 'g', 5, NOW()), -- 咖喱酱 (可能含大豆) +(103, 10, 150.00, 'ml', 3, NOW()), -- 椰奶 (可能含乳制品) +(103, 3, 80.00, 'g', NULL, NOW()), + + +(104, 11, 120.00, 'g', 6, NOW()), -- 意大利面 (含麦麸) +(104, 12, 150.00, 'g', NULL, NOW()), +(104, 13, 100.00, 'g', NULL, NOW()), +(104, 14, 20.00, 'g', 3, NOW()), -- 帕玛森芝士 (含乳制品) + + +(105, 15, 100.00, 'g', NULL, NOW()), +(105, 16, 80.00, 'g', NULL, NOW()), +(105, 17, 30.00, 'g', NULL, NOW()), +(105, 18, 40.00, 'g', 3, NOW()), -- 羊奶芝士 (含乳制品) +(105, 2, 15.00, 'ml', NULL, NOW()); + + +INSERT INTO fb_menu_and_outlet_relationship (FB_id, FB_outlet_id, FB_menu_id, created_at) VALUES +(1, 1001, 101, NOW()), +(1, 1001, 102, NOW()), +(1, 1001, 103, NOW()), +(1, 1002, 101, NOW()), +(1, 1002, 104, NOW()), +(1, 1002, 105, NOW()), +(2, 2001, 201, NOW()), +(2, 2001, 202, NOW()), +(2, 2001, 203, NOW()), +(2, 2002, 201, NOW()), +(2, 2002, 202, NOW()); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/47_alter_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/47_alter_muti_table.sql new file mode 100644 index 0000000..64b8088 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/47_alter_muti_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:create-table-and-insert-data +--comment: Create table and insert data +/* +INSERT INTO fb_operator (FB_id, FB_name, FB_type, FB_cuisine, created_at) VALUES +(1, 'Healthy Eater', 'Fast Food', 'Chinese', NOW()), +(2, 'Seafood World', 'Dinner', 'Cantonese', NOW()), +*/ + +INSERT INTO fb_outlets (FB_outlet_id, FB_id, FB_outlet_district, FB_outlet_address, FB_outlet_phone, created_at) VALUES + +(1001, 1, 'Central', 'Central Financial Street 88, 1st Floor', '2345-6789', NOW()), +(1002, 1, 'Causeway Bay', 'Causeway Bay Times Square 2nd Floor', '2345-6790', NOW()), + + +(2001, 2, 'Tsim Sha Tsui', 'Tsim Sha Tsui Guangdong Road 30', '3456-7890', NOW()), +(2002, 2, 'Sha Tin', 'Sha Tin New City Plaza 5th Floor', '3456-7891', NOW()); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/48_build_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/48_build_muti_table.sql new file mode 100644 index 0000000..ab373ea --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/48_build_muti_table.sql @@ -0,0 +1,85 @@ +--liquibase formatted sql +--changeset terence:create-organization-tables +--comment: Create organization related tables + + +DROP TABLE daily_nutrition_summary; + + +CREATE TABLE User_Group_Organization ( + User_Grp_Org_ID INT AUTO_INCREMENT PRIMARY KEY, + User_Grp_Org_Name VARCHAR(100) NOT NULL, + Creator_User_ID INT NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + + +CREATE TABLE User_Group_Members ( + User_Grp_Org_ID INT NOT NULL, + User_ID INT NOT NULL, + R_Ship_Creator VARCHAR(50), + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_Grp_Org_ID, User_ID), + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE TABLE User_Group_Admin ( + User_Grp_Org_ID INT NOT NULL, + Admin_User_ID INT NOT NULL, + Organization_Role VARCHAR(50) NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_Grp_Org_ID, Admin_User_ID), + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE TABLE User_Delegation ( + User_ID INT NOT NULL, + Delegate_To_User_ID INT NOT NULL, + Data_Group_ID INT NOT NULL, + Delegation_Start_Date DATE NOT NULL, + Delegation_Expiry_Date DATE NOT NULL, + Creation_Date_Time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (User_ID, Delegate_To_User_ID, Data_Group_ID) +); + + +CREATE TABLE daily_nutrition_summary ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + User_Grp_Org_ID INT, + date DATE NOT NULL, + visibility_level INT DEFAULT 1, + data_category VARCHAR(50) DEFAULT 'regular', + reference_group_id INT, + health_metrics_reference_id INT, + total_calories DECIMAL(10,2) DEFAULT 0, + total_protein DECIMAL(10,2) DEFAULT 0, + total_fat DECIMAL(10,2) DEFAULT 0, + total_carbs DECIMAL(10,2) DEFAULT 0, + total_sodium DECIMAL(10,2) DEFAULT 0, + total_potassium DECIMAL(10,2) DEFAULT 0, + total_calcium DECIMAL(10,2) DEFAULT 0, + total_phosphorus DECIMAL(10,2) DEFAULT 0, + total_iron DECIMAL(10,2) DEFAULT 0, + total_vitamin_a DECIMAL(10,2) DEFAULT 0, + total_vitamin_b1 DECIMAL(10,2) DEFAULT 0, + total_vitamin_b2 DECIMAL(10,2) DEFAULT 0, + total_niacin DECIMAL(10,2) DEFAULT 0, + total_vitamin_c DECIMAL(10,2) DEFAULT 0, + breakfast_calories DECIMAL(10,2) DEFAULT 0, + lunch_calories DECIMAL(10,2) DEFAULT 0, + dinner_calories DECIMAL(10,2) DEFAULT 0, + snack_calories DECIMAL(10,2) DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (User_Grp_Org_ID) REFERENCES User_Group_Organization(User_Grp_Org_ID) +); + + +CREATE INDEX idx_daily_nutrition_user_date ON daily_nutrition_summary(user_id, date); +CREATE INDEX idx_daily_nutrition_org_date ON daily_nutrition_summary(User_Grp_Org_ID, date); + + + diff --git a/src/main/resources/db/changelog/changes/enson_change/49_alter_muti_table.sql b/src/main/resources/db/changelog/changes/enson_change/49_alter_muti_table.sql new file mode 100644 index 0000000..7f1981e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/49_alter_muti_table.sql @@ -0,0 +1,71 @@ +--liquibase formatted sql +--changeset terence:create-organization-tables +--comment: Create organization related tables + + + +INSERT INTO User_Group_Organization (User_Grp_Org_ID, User_Grp_Org_Name, Creator_User_ID, Creation_Date_Time) +VALUES +(1, 'Aschool', 1001, NOW()), +(2, 'Bschool', 1002, NOW()), +(3, 'Cschool', 1003, NOW()), +(4, 'Dschool', 1004, NOW()), +(5, 'Eschool', 1005, NOW()); + + +INSERT INTO User_Group_Members (User_Grp_Org_ID, User_ID, R_Ship_Creator, Creation_Date_Time) +VALUES + +(1, 1001, 'Creator', NOW()), +(1, 1010, 'Student', NOW()), +(1, 1011, 'Student', NOW()), +(1, 1012, 'Teacher', NOW()), +(1, 1013, 'Teacher', NOW()), + +(2, 1002, 'Creator', NOW()), +(2, 1020, 'Student', NOW()), +(2, 1021, 'Student', NOW()), +(2, 1022, 'Teacher', NOW()), + +(3, 1003, 'Creator', NOW()), +(3, 1030, 'Student', NOW()), +(3, 1031, 'Student', NOW()), + +(4, 1004, 'Creator', NOW()), +(4, 1040, 'Student', NOW()), + +(5, 1005, 'Creator', NOW()), +(5, 1050, 'Student', NOW()); + + +INSERT INTO User_Group_Admin (User_Grp_Org_ID, Admin_User_ID, Organization_Role, Creation_Date_Time) +VALUES + +(1, 1001, 'President', NOW()), +(1, 1012, 'Dean', NOW()), +(1, 1013, 'Dean', NOW()), + +(2, 1002, 'President', NOW()), +(2, 1022, 'Dean', NOW()), + +(3, 1003, 'President', NOW()), +(3, 1031, 'President', NOW()), + +(4, 1004, 'President', NOW()), + +(5, 1005, 'President', NOW()); + + +INSERT INTO User_Delegation (User_ID, Delegate_To_User_ID, Data_Group_ID, Delegation_Start_Date, Delegation_Expiry_Date, Creation_Date_Time) +VALUES + +(1001, 1012, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), + +(1001, 1013, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 6 MONTH), NOW()), + +(1002, 1022, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), +(1002, 1022, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), NOW()), + +(1003, 1031, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 3 MONTH), NOW()), + +(1010, 1011, 4, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 7 DAY), NOW()); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/50_alter_table.sql b/src/main/resources/db/changelog/changes/enson_change/50_alter_table.sql new file mode 100644 index 0000000..9413a3a --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/50_alter_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:insert-sample-data +--comment: Insert sample data for testing + +ALTER TABLE `fhsmscdb`.`daily_nutrition_summary` +CHANGE COLUMN `created_at` `created` datetime, +CHANGE COLUMN `updated_at` `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN `createdBy` bigint DEFAULT NULL, +ADD COLUMN `version` int NOT NULL DEFAULT '0', +ADD COLUMN `modifiedBy` bigint DEFAULT NULL, +ADD COLUMN `deleted` tinyint(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/51_alter_i18n.sql b/src/main/resources/db/changelog/changes/enson_change/51_alter_i18n.sql new file mode 100644 index 0000000..4080519 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/51_alter_i18n.sql @@ -0,0 +1,35 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +UPDATE fb_menu SET Menu_Item_Description = CASE FB_menu_id + WHEN 101 THEN 'Pan-fried Salmon with Seasonal Vegetables' + WHEN 102 THEN 'Black Pepper Steak with Mushroom Sauce' + WHEN 103 THEN 'Thai Green Curry Chicken' + WHEN 104 THEN 'Spaghetti Bolognese with Parmesan Cheese' + WHEN 105 THEN 'Mediterranean Salad' + WHEN 201 THEN 'Steamed Sea Bass with Ginger and Spring Onion' + WHEN 202 THEN 'Braised Lion Head Meatballs' + WHEN 203 THEN 'Kung Pao Chicken with Peanuts' +END +WHERE FB_menu_id IN (101,102,103,104,105,201,202,203); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_menu', 'Menu_Item_Description', 101, 'zh', '香煎鮭魚配時蔬'), +('fb_menu', 'Menu_Item_Description', 102, 'zh', '黑椒牛排佐蘑菇醬'), +('fb_menu', 'Menu_Item_Description', 103, 'zh', '泰國青咖哩雞'), +('fb_menu', 'Menu_Item_Description', 104, 'zh', '義式肉醬麵配帕瑪森起司'), +('fb_menu', 'Menu_Item_Description', 105, 'zh', '地中海風味沙拉'), +('fb_menu', 'Menu_Item_Description', 201, 'zh', '清蒸鱸魚配薑蔥'), +('fb_menu', 'Menu_Item_Description', 202, 'zh', '紅燒獅子頭'), +('fb_menu', 'Menu_Item_Description', 203, 'zh', '宮保雞丁配花生米'); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_menu', 'Menu_Item_Description', 101, 'en', 'Pan-fried Salmon with Seasonal Vegetables'), +('fb_menu', 'Menu_Item_Description', 102, 'en', 'Black Pepper Steak with Mushroom Sauce'), +('fb_menu', 'Menu_Item_Description', 103, 'en', 'Thai Green Curry Chicken'), +('fb_menu', 'Menu_Item_Description', 104, 'en', 'Spaghetti Bolognese with Parmesan Cheese'), +('fb_menu', 'Menu_Item_Description', 105, 'en', 'Mediterranean Salad'), +('fb_menu', 'Menu_Item_Description', 201, 'en', 'Steamed Sea Bass with Ginger and Spring Onion'), +('fb_menu', 'Menu_Item_Description', 202, 'en', 'Braised Lion Head Meatballs'), +('fb_menu', 'Menu_Item_Description', 203, 'en', 'Kung Pao Chicken with Peanuts'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/51_create_i18n.sql b/src/main/resources/db/changelog/changes/enson_change/51_create_i18n.sql new file mode 100644 index 0000000..a34fb27 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/51_create_i18n.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +CREATE TABLE `I18n` ( + `id` int NOT NULL AUTO_INCREMENT, + `table_name` varchar(50) NOT NULL, + `field_name` varchar(50) NOT NULL , + `record_id` int NOT NULL, + `language` varchar(10) NOT NULL, + `value` text NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `modifiedBy` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `UK_translation` (`table_name`, `field_name`, `record_id`, `language`), + KEY `IDX_table_field` (`table_name`, `field_name`), + KEY `IDX_language` (`language`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/53_alter_i18n.sql b/src/main/resources/db/changelog/changes/enson_change/53_alter_i18n.sql new file mode 100644 index 0000000..d4d0b34 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/53_alter_i18n.sql @@ -0,0 +1,31 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +UPDATE fb_outlets SET FB_outlet_district = CASE id + WHEN 1 THEN 'Admiralty' + WHEN 2 THEN 'Wan Chai' + WHEN 3 THEN 'Kwun Tong' + WHEN 4 THEN 'Kwun Tong' +END +WHERE id IN (1,2,3,4); + +UPDATE fb_outlets SET FB_outlet_address = CASE id + WHEN 1 THEN 'Shop A, No.1-41, G/F, Admiralty Centre, 18 Harcourt Road' + WHEN 2 THEN 'Shop G11-17 ,G/F, C.c. Wu Building,11-17, 302-308, Hennessy Road' + WHEN 3 THEN 'Speedy Industrial Building, 2nd Floor, Room B, 114, Kwun Tong Road' + WHEN 4 THEN 'Shop No. L2-1 Level 2, apm, 418 Kwun Tong Rd' +END +WHERE id IN (1,2,3,4); +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 1, 'zh', '中環'), +('fb_outlets', 'FB_outlet_district', 2, 'zh', '灣仔'), +('fb_outlets', 'FB_outlet_district', 3, 'zh', '觀塘'), +('fb_outlets', 'FB_outlet_district', 4, 'zh', '觀塘'); + + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_address', 1, 'en', 'Shop A, No.1-41, G/F, Admiralty Centre, 18 Harcourt Road'), +('fb_outlets', 'FB_outlet_address', 2, 'en', 'Shop G11-17 ,G/F, C.c. Wu Building,11-17, 302-308, Hennessy Road'), +('fb_outlets', 'FB_outlet_address', 3, 'en', 'Speedy Industrial Building, 2nd Floor, Room B, 114, Kwun Tong Road'), +('fb_outlets', 'FB_outlet_address', 4, 'en', 'Shop No. L2-1 Level 2, apm, 418 Kwun Tong Rd'); diff --git a/src/main/resources/db/changelog/changes/enson_change/54_alter_i18n.sql b/src/main/resources/db/changelog/changes/enson_change/54_alter_i18n.sql new file mode 100644 index 0000000..e7b54b2 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/54_alter_i18n.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 1, 'en', 'Admiralty'), +('fb_outlets', 'FB_outlet_district', 2, 'en', 'Wan Chai'), +('fb_outlets', 'FB_outlet_district', 3, 'en', 'Kwun Tong'), +('fb_outlets', 'FB_outlet_district', 4, 'en', 'Kwun Tong'); + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_address', 1, 'zh', '夏愨道18號海富中心地下1-41號A鋪'), +('fb_outlets', 'FB_outlet_address', 2, 'zh', '灣仔軒尼詩道302-308號皇后大廈地下G11-17號鋪'), +('fb_outlets', 'FB_outlet_address', 3, 'zh', '觀塘鯉魚門道114號迅達工業大廈2樓B室'), +('fb_outlets', 'FB_outlet_address', 4, 'zh', '觀塘鯉魚門道418號apm2樓L2-1號鋪'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/55_alter_i18n.sql b/src/main/resources/db/changelog/changes/enson_change/55_alter_i18n.sql new file mode 100644 index 0000000..64105b1 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/55_alter_i18n.sql @@ -0,0 +1,24 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + + + +INSERT INTO i18n (table_name, field_name, record_id, language, value) VALUES +('fb_outlets', 'FB_outlet_district', 7, 'en', 'Central'), +('fb_outlets', 'FB_outlet_district', 8, 'en', 'Causeway Bay'), +('fb_outlets', 'FB_outlet_district', 9, 'en', 'Tsim Sha Tsui'), +('fb_outlets', 'FB_outlet_district', 10, 'en', 'Sha Tin'), +('fb_outlets', 'FB_outlet_district', 7, 'zh', '中環'), +('fb_outlets', 'FB_outlet_district', 8, 'zh', '銅鑼灣'), +('fb_outlets', 'FB_outlet_district', 9, 'zh', '尖沙咀'), +('fb_outlets', 'FB_outlet_district', 10, 'zh', '沙田'), + +('fb_outlets', 'FB_outlet_address', 7, 'en', 'Central Financial Street 88, 1st Floor'), +('fb_outlets', 'FB_outlet_address', 8, 'en', 'Causeway Bay Times Square 2nd Floor'), +('fb_outlets', 'FB_outlet_address', 9, 'en', 'Tsim Sha Tsui Guangdong Road 30'), +('fb_outlets', 'FB_outlet_address', 10, 'en', 'Sha Tin New City Plaza 5th Floor'), +('fb_outlets', 'FB_outlet_address', 7, 'zh', '金融街88號1樓'), +('fb_outlets', 'FB_outlet_address', 8, 'zh', '時代廣場2樓'), +('fb_outlets', 'FB_outlet_address', 9, 'zh', '廣東道30號'), +('fb_outlets', 'FB_outlet_address', 10, 'zh', '新城市廣場52樓'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/56_create_table.sql b/src/main/resources/db/changelog/changes/enson_change/56_create_table.sql new file mode 100644 index 0000000..61d17c3 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/56_create_table.sql @@ -0,0 +1,15 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + + + +CREATE TABLE `UserAccessControl` ( + `UserId` int NOT NULL AUTO_INCREMENT, + `FunctionId` int NOT NULL, + `Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `Modified` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `CreatedBy` int DEFAULT NULL, + `ModifiedBy` int DEFAULT NULL, + PRIMARY KEY (`UserId`, `FunctionId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/58_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/58_insert_data.sql new file mode 100644 index 0000000..35e02d7 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/58_insert_data.sql @@ -0,0 +1,63 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', 'USER_MANAGEMENT', 'Maintain user master data'), +(2, 'VIEW_USER', 'View User', 'USER_MANAGEMENT', 'View user master data'), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', 'USER_MANAGEMENT', 'Maintain user group master data'), +(4, 'VIEW_USER_GROUP', 'View User Group', 'USER_MANAGEMENT', 'View user group master data'), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu', 'FOOD_MANAGEMENT', 'Maintain food and menu data'), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', 'FOOD_MANAGEMENT', 'View food and menu data'), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', 'FOOD_MANAGEMENT', 'Maintain food intake records'), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', 'FOOD_MANAGEMENT', 'View food intake records'), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', 'NUTRITION_MANAGEMENT', 'Maintain nutrition standards'), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', 'NUTRITION_MANAGEMENT', 'View nutrition standards'), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', 'NUTRITION_MANAGEMENT', 'Maintain nutrition analysis'), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', 'NUTRITION_MANAGEMENT', 'View nutrition analysis'), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', 'HEALTH_MANAGEMENT', 'Maintain health development data'), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', 'HEALTH_MANAGEMENT', 'View health development data'), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', 'HEALTH_MANAGEMENT', 'Maintain allergy data'), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', 'HEALTH_MANAGEMENT', 'View allergy data'), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', 'AI_MANAGEMENT', 'Maintain food AI models'), +(18, 'VIEW_AI_MODEL', 'View AI Model', 'AI_MANAGEMENT', 'View food AI models'), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', 'REPORT_MANAGEMENT', 'Generate nutrition trend analysis reports'), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', 'REPORT_MANAGEMENT', 'Generate health statistics reports'), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', 'REPORT_MANAGEMENT', 'Generate food leftover analysis reports'), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', 'FB_MANAGEMENT', 'Maintain F&B operator data'), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', 'FB_MANAGEMENT', 'View F&B operator data'), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', 'FB_MANAGEMENT', 'Maintain F&B outlet data'), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', 'FB_MANAGEMENT', 'View F&B outlet data'), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', 'ADVICE_MANAGEMENT', 'Maintain dietary advice data'), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', 'ADVICE_MANAGEMENT', 'View dietary advice data'), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', 'ACCESS_MANAGEMENT', 'Maintain access control settings'), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', 'ACCESS_MANAGEMENT', 'View access control settings'), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', 'ACCESS_MANAGEMENT', 'Maintain access delegation settings'), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', 'ACCESS_MANAGEMENT', 'View access delegation settings'); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/59_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/59_insert_data.sql new file mode 100644 index 0000000..9f6c03a --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/59_insert_data.sql @@ -0,0 +1,63 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/60_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/60_insert_data.sql new file mode 100644 index 0000000..aff3bc6 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/60_insert_data.sql @@ -0,0 +1,67 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Insert user authority mapping + +-- System Admin +INSERT INTO user_authority (userId, authId) VALUES +-- user and access control management +(2, 1), -- MAINTAIN_USER +(1, 2), -- VIEW_USER +(1, 3), -- MAINTAIN_GROUP +(1, 4), -- VIEW_USER_GROUP +(1, 28), -- MAINTAIN_ACCESS_CONTROL +(1, 29), -- VIEW_ACCESS_CONTROL +(1, 30), -- MAINTAIN_ACCESS_DELEGATION +(1, 31), -- VIEW_ACCESS_DELEGATION +-- AI和餐饮管理 +(1, 17), -- MAINTAIN_AI_MODEL +(1, 18), -- VIEW_AI_MODEL +(1, 22), -- MAINTAIN_FB_OPERATOR +(1, 23), -- VIEW_FB_OPERATOR +(1, 24), -- MAINTAIN_FB_OUTLET +(1, 25); -- VIEW_FB_OUTLET + +-- School Admin +INSERT INTO user_authority (userId, authId) VALUES +-- basic view permission +(2, 2), -- VIEW_USER +(2, 4), -- VIEW_USER_GROUP +(2, 29), -- VIEW_ACCESS_CONTROL +-- report permission +(2, 19), -- G_NUTRITION_TREND_REPORT +(2, 20), -- G_HEALTH_STATISTICS_REPORT +(2, 21), -- G_FOOD_LEFTOVER_REPORT +-- food and menu view permission +(2, 6), -- VIEW_FOOD_MENU +(2, 23), -- VIEW_FB_OPERATOR +(2, 25); -- VIEW_FB_OUTLET + +-- Student User +INSERT INTO user_authority (userId, authId) VALUES +-- personal data management +(9, 7), -- MAINTAIN_FOOD_INTAKE +(9, 8), -- VIEW_FOOD_INTAKE +(9, 13), -- MAINTAIN_HEALTH_DATA +(9, 14), -- VIEW_HEALTH_DATA +(9, 15), -- MAINTAIN_ALLERGY_DATA +(9, 16), -- VIEW_ALLERGY_DATA +-- view permission +(9, 6), -- VIEW_FOOD_MENU +(9, 10), -- VIEW_NUTRITION_STANDARD +(9, 12), -- VIEW_NUTRITION_ANALYSIS +(9, 27); -- VIEW_DIETARY_ADVICE + +-- Nutritionist/Health Advisor +INSERT INTO user_authority (userId, authId) VALUES +-- nutrition and health management +(8, 11), -- MAINTAIN_NUTRITION_ANALYSIS +(8, 12), -- VIEW_NUTRITION_ANALYSIS +(8, 26), -- MAINTAIN_DIETARY_ADVICE +(8, 27), -- VIEW_DIETARY_ADVICE +-- data view permission +(8, 14), -- VIEW_HEALTH_DATA +(8, 16), -- VIEW_ALLERGY_DATA +(8, 8), -- VIEW_FOOD_INTAKE +-- report generation +(8, 19), -- G_NUTRITION_TREND_REPORT +(8, 20); -- G_HEALTH_STATISTICS_REPORT \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/61_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/61_insert_data.sql new file mode 100644 index 0000000..497e21f --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/61_insert_data.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql +--changeset terence:create-i18n-table +--comment: Create i18n table + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- Access Control Management +(32, 'MAINTAIN_MENU_ITEM', 'Maintain Menu Item', null, null), +(33, 'VIEW_MENU_ITEM', 'View Menu Item', null, null), +(34, 'MAINTAIN_INGREDIENT', 'Maintain Ingredient', null, null), +(35, 'VIEW_INGREDIENT', 'View Ingredient', null, null), +(36, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', null, null), +(37, 'VIEW_MENU_OUTLET', 'View Menu Outlet', null, null); diff --git a/src/main/resources/db/changelog/changes/enson_change/62_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/62_insert_data.sql new file mode 100644 index 0000000..b503cba --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/62_insert_data.sql @@ -0,0 +1,61 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +-- 插入权限数据(添加条件检查) +INSERT INTO authority (id, authority, name, module, description) +SELECT 38, 'MAINTAIN_FB_OPERATOR', 'Maintain F&B Operator', 'F&B', 'Permission to maintain F&B operator information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_FB_OPERATOR'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 39, 'VIEW_FB_OPERATOR', 'View F&B Operator', 'F&B', 'Permission to view F&B operator information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_FB_OPERATOR'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 40, 'MAINTAIN_FB_OUTLET', 'Maintain F&B Outlet', 'F&B', 'Permission to maintain F&B outlet information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_FB_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 41, 'VIEW_FB_OUTLET', 'View F&B Outlet', 'F&B', 'Permission to view F&B outlet information' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_FB_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 42, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', 'F&B', 'Permission to maintain menu-outlet relationship' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'MAINTAIN_MENU_OUTLET'); + +INSERT INTO authority (id, authority, name, module, description) +SELECT 43, 'VIEW_MENU_OUTLET', 'View Menu Outlet', 'F&B', 'Permission to view menu-outlet relationship' +WHERE NOT EXISTS (SELECT 1 FROM authority WHERE authority = 'VIEW_MENU_OUTLET'); + +-- 插入用户权限关联(添加条件检查) +INSERT INTO user_authority (userId, authId) +SELECT 6, 38 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 38); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 39 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 39); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 40 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 40); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 41 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 41); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 42 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 42); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 43 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 43); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 32 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 32); + +INSERT INTO user_authority (userId, authId) +SELECT 6, 33 +WHERE NOT EXISTS (SELECT 1 FROM user_authority WHERE userId = 6 AND authId = 33); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/63_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/63_insert_data.sql new file mode 100644 index 0000000..f8fde29 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/63_insert_data.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +-- 插入权限数据(使用INSERT IGNORE避免重复) +INSERT IGNORE INTO authority (id, authority, name, module, description) +VALUES +(38, 'MAINTAIN_FB_OPERATOR', 'Maintain F&B Operator', 'F&B', 'Permission to maintain F&B operator information'), +(39, 'VIEW_FB_OPERATOR', 'View F&B Operator', 'F&B', 'Permission to view F&B operator information'), +(40, 'MAINTAIN_FB_OUTLET', 'Maintain F&B Outlet', 'F&B', 'Permission to maintain F&B outlet information'), +(41, 'VIEW_FB_OUTLET', 'View F&B Outlet', 'F&B', 'Permission to view F&B outlet information'), +(42, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', 'F&B', 'Permission to maintain menu-outlet relationship'), +(43, 'VIEW_MENU_OUTLET', 'View Menu Outlet', 'F&B', 'Permission to view menu-outlet relationship'); + +-- 插入用户权限关联(使用INSERT IGNORE避免重复) +INSERT IGNORE INTO user_authority (userId, authId) +VALUES +(6, 38), +(6, 39), +(6, 40), +(6, 41), +(6, 42), +(6, 43), +(6, 32), +(6, 33); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/64_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/64_insert_data.sql new file mode 100644 index 0000000..785fb95 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/64_insert_data.sql @@ -0,0 +1,66 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); + + diff --git a/src/main/resources/db/changelog/changes/enson_change/65_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/65_insert_data.sql new file mode 100644 index 0000000..785fb95 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/65_insert_data.sql @@ -0,0 +1,66 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +drop table if exists authority; +CREATE TABLE `authority` ( + `id` int NOT NULL AUTO_INCREMENT, + `authority` varchar(100) NOT NULL, + `name` varchar(255) NOT NULL, + `module` varchar(100) DEFAULT NULL, + `description` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_authority` (`authority`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- User Management +(1, 'MAINTAIN_USER', 'Maintain User', null, null), +(2, 'VIEW_USER', 'View User', null, null), +(3, 'MAINTAIN_GROUP', 'Maintain User Group', null, null), +(4, 'VIEW_USER_GROUP', 'View User Group', null, null), + +-- Food and Menu Management +(5, 'MAINTAIN_FOOD_MENU', 'Maintain Food Menu' , null, null), +(6, 'VIEW_FOOD_MENU', 'View Food Menu', null, null), +(7, 'MAINTAIN_FOOD_INTAKE', 'Maintain Food Intake', null, null), +(8, 'VIEW_FOOD_INTAKE', 'View Food Intake', null, null), + +-- Nutrition Management +(9, 'MAINTAIN_NUTRITION_STANDARD', 'Maintain Nutrition Standard', null, null), +(10, 'VIEW_NUTRITION_STANDARD', 'View Nutrition Standard', null, null), +(11, 'MAINTAIN_NUTRITION_ANALYSIS', 'Maintain Nutrition Analysis', null, null), +(12, 'VIEW_NUTRITION_ANALYSIS', 'View Nutrition Analysis', null, null), + +-- Health Data Management +(13, 'MAINTAIN_HEALTH_DATA', 'Maintain Health Data', null, null), +(14, 'VIEW_HEALTH_DATA', 'View Health Data', null, null), +(15, 'MAINTAIN_ALLERGY_DATA', 'Maintain Allergy Data', null, null), +(16, 'VIEW_ALLERGY_DATA', 'View Allergy Data', null, null), + +-- AI Model Management +(17, 'MAINTAIN_AI_MODEL', 'Maintain AI Model', null, null), +(18, 'VIEW_AI_MODEL', 'View AI Model', null, null), + +-- Report Generation +(19, 'G_NUTRITION_TREND_REPORT', 'Generate Nutrition Trend Report', null, null), +(20, 'G_HEALTH_STATISTICS_REPORT', 'Generate Health Statistics Report', null, null), +(21, 'G_FOOD_LEFTOVER_REPORT', 'Generate Food Leftover Report', null, null), + +-- F&B Management +(22, 'MAINTAIN_FB_OPERATOR', 'Maintain Food Operator', null, null), +(23, 'VIEW_FB_OPERATOR', 'View Food Operator', null, null), +(24, 'MAINTAIN_FB_OUTLET', 'Maintain Food Outlet', null, null), +(25, 'VIEW_FB_OUTLET', 'View Food Outlet', null, null), + +-- Dietary Advice Management +(26, 'MAINTAIN_DIETARY_ADVICE', 'Maintain Dietary Advice', null, null), +(27, 'VIEW_DIETARY_ADVICE', 'View Dietary Advice', null, null), + +-- Access Control Management +(28, 'MAINTAIN_ACCESS_CONTROL', 'Maintain Access Control', null, null), +(29, 'VIEW_ACCESS_CONTROL', 'View Access Control', null, null), +(30, 'MAINTAIN_ACCESS_DELEGATION', 'Maintain Access Delegation', null, null), +(31, 'VIEW_ACCESS_DELEGATION', 'View Access Delegation', null, null); + + diff --git a/src/main/resources/db/changelog/changes/enson_change/66_insert_data.sql b/src/main/resources/db/changelog/changes/enson_change/66_insert_data.sql new file mode 100644 index 0000000..bdf5207 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/66_insert_data.sql @@ -0,0 +1,16 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +INSERT INTO authority (id, authority, name, module, description) VALUES +-- Access Control Management +(32, 'MAINTAIN_MENU_ITEM', 'Maintain Menu Item', null, null), +(33, 'VIEW_MENU_ITEM', 'View Menu Item', null, null), +(34, 'MAINTAIN_INGREDIENT', 'Maintain Ingredient', null, null), +(35, 'VIEW_INGREDIENT', 'View Ingredient', null, null), +(36, 'MAINTAIN_MENU_OUTLET', 'Maintain Menu Outlet', null, null), +(37, 'VIEW_MENU_OUTLET', 'View Menu Outlet', null, null); + + + + diff --git a/src/main/resources/db/changelog/changes/enson_change/67_edit_table.sql b/src/main/resources/db/changelog/changes/enson_change/67_edit_table.sql new file mode 100644 index 0000000..4e82707 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/67_edit_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + +ALTER TABLE `FB_Operator` DROP COLUMN created_at; + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `FB_Operator` +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/src/main/resources/db/changelog/changes/enson_change/68_edit_table.sql b/src/main/resources/db/changelog/changes/enson_change/68_edit_table.sql new file mode 100644 index 0000000..7304e84 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/68_edit_table.sql @@ -0,0 +1,17 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + +ALTER TABLE `fb_outlets` DROP COLUMN created_at; + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `fb_outlets` +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/src/main/resources/db/changelog/changes/enson_change/69_edit_table.sql b/src/main/resources/db/changelog/changes/enson_change/69_edit_table.sql new file mode 100644 index 0000000..5806a6e --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/69_edit_table.sql @@ -0,0 +1,13 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + + +-- 添加 BaseEntity 需要的字段 +ALTER TABLE `i18n` + +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; + + diff --git a/src/main/resources/db/changelog/changes/enson_change/70_edit_table.sql b/src/main/resources/db/changelog/changes/enson_change/70_edit_table.sql new file mode 100644 index 0000000..adf3ea7 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/70_edit_table.sql @@ -0,0 +1,72 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + + + +INSERT INTO user_authority (userId, authId) VALUES +-- ARestaurantManager (userid 6) - 可以查看和编辑 +(6, 5), -- MAINTAIN_FOOD_MENU +(6, 6), -- VIEW_FOOD_MENU +(6, 7), -- MAINTAIN_FOOD_INTAKE +(6, 8), -- VIEW_FOOD_INTAKE +(6, 32), -- MAINTAIN_MENU_ITEM +(6, 33), -- VIEW_MENU_ITEM +(6, 34), -- MAINTAIN_INGREDIENT +(6, 35), -- VIEW_INGREDIENT +(6, 36), -- MAINTAIN_MENU_OUTLET +(6, 37), -- VIEW_MENU_OUTLET + +-- ARestaurantclerk (userid 8) - 只能查看 +(8, 6), -- VIEW_FOOD_MENU +(8, 8), -- VIEW_FOOD_INTAKE +(8, 33), -- VIEW_MENU_ITEM +(8, 35), -- VIEW_INGREDIENT +(8, 37), -- VIEW_MENU_OUTLET + +-- BRestaurantManager (userid 10) - 可以查看和编辑 +(10, 5), -- MAINTAIN_FOOD_MENU +(10, 6), -- VIEW_FOOD_MENU +(10, 7), -- MAINTAIN_FOOD_INTAKE +(10, 8), -- VIEW_FOOD_INTAKE +(10, 32), -- MAINTAIN_MENU_ITEM +(10, 33), -- VIEW_MENU_ITEM +(10, 34), -- MAINTAIN_INGREDIENT +(10, 35), -- VIEW_INGREDIENT +(10, 36), -- MAINTAIN_MENU_OUTLET +(10, 37), -- VIEW_MENU_OUTLET + +-- BRestaurantclerk (userid 11) - 只能查看 +(11, 6), -- VIEW_FOOD_MENU +(11, 8), -- VIEW_FOOD_INTAKE +(11, 33), -- VIEW_MENU_ITEM +(11, 35), -- VIEW_INGREDIENT +(11, 37), -- VIEW_MENU_OUTLET + +-- ASchoolheadmaster (userid 12) - 可以查看和编辑 +(12, 13), -- MAINTAIN_HEALTH_DATA +(12, 14), -- VIEW_HEALTH_DATA +(12, 15), -- MAINTAIN_ALLERGY_DATA +(12, 16), -- VIEW_ALLERGY_DATA +(12, 26), -- MAINTAIN_DIETARY_ADVICE +(12, 27), -- VIEW_DIETARY_ADVICE +(12, 20), -- G_HEALTH_STATISTICS_REPORT + +-- ASchoolteacher (userid 13) - 只能查看 +(13, 14), -- VIEW_HEALTH_DATA +(13, 16), -- VIEW_ALLERGY_DATA +(13, 27), -- VIEW_DIETARY_ADVICE + +-- BSchoolheadmaster (userid 14) - 可以查看和编辑 +(14, 13), -- MAINTAIN_HEALTH_DATA +(14, 14), -- VIEW_HEALTH_DATA +(14, 15), -- MAINTAIN_ALLERGY_DATA +(14, 16), -- VIEW_ALLERGY_DATA +(14, 26), -- MAINTAIN_DIETARY_ADVICE +(14, 27), -- VIEW_DIETARY_ADVICE +(14, 20), -- G_HEALTH_STATISTICS_REPORT + +-- BSchoolteacher (userid 15) - 只能查看 +(15, 14), -- VIEW_HEALTH_DATA +(15, 16), -- VIEW_ALLERGY_DATA +(15, 27); -- VIEW_DIETARY_ADVICE \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/71_edit_table.sql b/src/main/resources/db/changelog/changes/enson_change/71_edit_table.sql new file mode 100644 index 0000000..5ed5092 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/71_edit_table.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:62-insert-fb-permissions +--comment: Insert F&B related permissions and user authorities + +INSERT INTO user_authority (userId, authId) VALUES +-- ARestaurantManager (userid 6) - 可以查看和编辑 +(6, 22), -- MAINTAIN_FB_OPERATOR +(6, 23), -- VIEW_FB_OPERATOR +(6, 24), -- MAINTAIN_FB_OUTLET +(6, 25), -- VIEW_FB_OUTLET + +-- ARestaurantclerk (userid 8) - 只能查看 +(8, 23), -- VIEW_FOOD_MENU +(8, 25), -- VIEW_FOOD_INTAKE + +-- BRestaurantManager (userid 10) - 可以查看和编辑 +(10, 22), -- MAINTAIN_FB_OPERATOR +(10, 23), -- VIEW_FB_OPERATOR +(10, 24), -- MAINTAIN_FB_OUTLET +(10, 25), -- VIEW_FB_OUTLET + +-- BRestaurantclerk (userid 11) - 只能查看 +(11, 23), -- VIEW_FOOD_MENU +(11, 25); -- VIEW_FOOD_INTAKE + diff --git a/src/main/resources/db/changelog/changes/enson_change/72_create_table.sql b/src/main/resources/db/changelog/changes/enson_change/72_create_table.sql new file mode 100644 index 0000000..a6fea54 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/72_create_table.sql @@ -0,0 +1,24 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +CREATE TABLE `fb_operator_and_organization_relationship` ( + `id` int NOT NULL AUTO_INCREMENT, + `FB_id` int NOT NULL, + `User_Grp_Org_ID` int NOT NULL, + `created` timestamp NULL, + `createdBy` varchar(255) NULL, + `modified` timestamp NULL, + `modifiedBy` varchar(255) NULL, + `deleted` bit(1) DEFAULT 0, + `version` int DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO `fb_operator_and_organization_relationship` +(id, FB_id, User_Grp_Org_ID) VALUES +-- User Management +(1, 1, 1), +(2, 2, 2), +(3, 3, 2), +(4, 4, 1); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/73_ALTER_table.sql b/src/main/resources/db/changelog/changes/enson_change/73_ALTER_table.sql new file mode 100644 index 0000000..05105fa --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/73_ALTER_table.sql @@ -0,0 +1,8 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_authority +DROP PRIMARY KEY, +ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY FIRST, +ADD UNIQUE KEY unique_user_auth (userId, authId); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/74_ALTER_table.sql b/src/main/resources/db/changelog/changes/enson_change/74_ALTER_table.sql new file mode 100644 index 0000000..51ba038 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/74_ALTER_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_authority +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/75_ALTER_table.sql b/src/main/resources/db/changelog/changes/enson_change/75_ALTER_table.sql new file mode 100644 index 0000000..e661d5d --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/75_ALTER_table.sql @@ -0,0 +1,11 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +ALTER TABLE user_group_members +ADD COLUMN `created` timestamp NULL, +ADD COLUMN `createdBy` varchar(255) NULL, +ADD COLUMN `modified` timestamp NULL, +ADD COLUMN `modifiedBy` varchar(255) NULL, +ADD COLUMN `deleted` bit(1) DEFAULT 0, +ADD COLUMN `version` int DEFAULT 0; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/76_ALTER_table.sql b/src/main/resources/db/changelog/changes/enson_change/76_ALTER_table.sql new file mode 100644 index 0000000..653520d --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/76_ALTER_table.sql @@ -0,0 +1,25 @@ +--liquibase formatted sql +--changeset terence:72-create-fb_operator_and_organization_relationship-table +--comment: Insert F&B related permissions and user authorities + +-- First, drop the foreign key constraint +ALTER TABLE user_group_members +DROP FOREIGN KEY user_group_members_ibfk_1; + +-- Then drop the primary key +ALTER TABLE user_group_members +DROP PRIMARY KEY; + +-- Add the new id column +ALTER TABLE user_group_members +ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY; + +-- Re-add the foreign key constraint +ALTER TABLE user_group_members +ADD CONSTRAINT user_group_members_ibfk_1 +FOREIGN KEY (User_Grp_Org_ID) +REFERENCES user_group_organization (User_Grp_Org_ID); + +-- Add unique constraint for the original composite key +ALTER TABLE user_group_members +ADD CONSTRAINT uk_org_user UNIQUE (User_Grp_Org_ID, User_ID); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/77_create_table.sql b/src/main/resources/db/changelog/changes/enson_change/77_create_table.sql new file mode 100644 index 0000000..b357104 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/77_create_table.sql @@ -0,0 +1,26 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table +ALTER TABLE fb_menu + Add category VARCHAR(50), + Add price DECIMAL(10,2), + Add is_vegetarian BOOLEAN DEFAULT FALSE, + Add is_vegan BOOLEAN DEFAULT FALSE, + Add is_gluten_free BOOLEAN DEFAULT FALSE, + Add is_spicy BOOLEAN DEFAULT FALSE + + + +--changeset yourname:004-create-menu_embedding-table +CREATE TABLE menu_item_base_embedding ( + embedding_id INT NOT NULL AUTO_INCREMENT, + FB_menu_id INT NOT NULL, + embedding_vector JSON NOT NULL, + embedding_text TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (embedding_id), + FOREIGN KEY (FB_menu_id) REFERENCES fb_menu(FB_menu_id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/78_create_table.sql b/src/main/resources/db/changelog/changes/enson_change/78_create_table.sql new file mode 100644 index 0000000..7fb99a6 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/78_create_table.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table +CREATE TABLE fboperator ( + id INT AUTO_INCREMENT PRIMARY KEY, + FB_name VARCHAR(255) NOT NULL, + FB_operator_address VARCHAR(255), + FB_operator_phone VARCHAR(255), + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); diff --git a/src/main/resources/db/changelog/changes/enson_change/79_alter_table.sql b/src/main/resources/db/changelog/changes/enson_change/79_alter_table.sql new file mode 100644 index 0000000..54efd95 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/79_alter_table.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table + +CREATE TABLE fb_operator_backup AS SELECT * FROM fb_operator; + + +ALTER TABLE fb_operator +ADD COLUMN FB_operator_id INT NULL AFTER FB_id; + + +ALTER TABLE fb_operator +CHANGE COLUMN FB_id id INT AUTO_INCREMENT; + +RENAME TABLE fb_operator TO fb_brand; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/enson_change/80_create_table.sql b/src/main/resources/db/changelog/changes/enson_change/80_create_table.sql new file mode 100644 index 0000000..c1ff9f9 --- /dev/null +++ b/src/main/resources/db/changelog/changes/enson_change/80_create_table.sql @@ -0,0 +1,30 @@ +--liquibase formatted sql +--changeset terence:77-create-embedding-table +--comment: Insert F&B related permissions and user authorities + +--liquibase formatted sql +--changeset yourname:001-create-menu_item-table + +CREATE TABLE fb_group ( + id INT AUTO_INCREMENT PRIMARY KEY, + FB_brand_id INT NOT NULL, + FB_group_name VARCHAR(255) NOT NULL, + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); +CREATE TABLE fb_group_relationship ( + id INT AUTO_INCREMENT PRIMARY KEY, + Fb_brand_id INT NOT NULL, + FB_group_id INT NOT NULL, + FB_menu_id INT NOT NULL, + created TIMESTAMP, + createdBy VARCHAR(255), + modified TIMESTAMP, + modifiedBy VARCHAR(255), + deleted BIT(1), + version INT +); diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..b5832ba --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,3 @@ +databaseChangeLog: + - includeAll: + path: classpath:/db/changelog/changes \ No newline at end of file diff --git a/src/main/resources/log4j2-prod-linux.yml b/src/main/resources/log4j2-prod-linux.yml new file mode 100644 index 0000000..cfdd364 --- /dev/null +++ b/src/main/resources/log4j2-prod-linux.yml @@ -0,0 +1,30 @@ +Configutation: + name: Prod-Default + Properties: + Property: + name: log_location + value: /usr/springboot/logs/ + Appenders: + RollingFile: + - name: AllRollingFile_Appender + fileName: ${log_location}fhsmsc-all.log + filePattern: ${log_location}fhsmsc-all-%d{yyyy-MM-dd}.log.gz + PatternLayout: + Pattern: "%d %p [%l] - %m%n" + Policies: + TimeBasedTriggeringPolicy: + interval: 1 + modulate: true + DefaultRolloverStrategy: + Delete: + basePath: ${log_location} + maxDepth: 1 + IfFileName: + glob: fhsmsc-all-*.log.gz + IfLastModified: + age: P40D + Loggers: + Root: + level: info + AppenderRef: + - ref: AllRollingFile_Appender \ No newline at end of file diff --git a/src/main/resources/log4j2-prod-win.yml b/src/main/resources/log4j2-prod-win.yml new file mode 100644 index 0000000..e90a583 --- /dev/null +++ b/src/main/resources/log4j2-prod-win.yml @@ -0,0 +1,23 @@ +Configutation: + name: Prod-Default + Properties: + Property: + name: log_location + value: C:/workspace/ + Appenders: + RollingFile: + name: RollingFile_Appender + fileName: ${log_location}fhsmsc-all.log + filePattern: ${log_location}fhsmsc-all.log.%i.gz + PatternLayout: + Pattern: "%d %p [%l] - %m%n" + Policies: + SizeBasedTriggeringPolicy: + size: 4096KB + DefaultRollOverStrategy: + max: 99 + Loggers: + Root: + level: info + AppenderRef: + - ref: RollingFile_Appender \ No newline at end of file diff --git a/src/main/resources/log4j2.yml b/src/main/resources/log4j2.yml new file mode 100644 index 0000000..1d9a0cd --- /dev/null +++ b/src/main/resources/log4j2.yml @@ -0,0 +1,17 @@ +Configutation: + name: Default + Properties: + Property: + name: log_pattern + value: "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex" + Appenders: + Console: + name: Console_Appender + target: SYSTEM_OUT + PatternLayout: + pattern: ${log_pattern} + Loggers: + Root: + level: info + AppenderRef: + - ref: Console_Appender \ No newline at end of file diff --git a/src/test/java/com/ffii/ars/FhsmsCApplicationTests.java b/src/test/java/com/ffii/ars/FhsmsCApplicationTests.java new file mode 100644 index 0000000..7ae2c11 --- /dev/null +++ b/src/test/java/com/ffii/ars/FhsmsCApplicationTests.java @@ -0,0 +1,13 @@ +package com.ffii.ars; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class FhsmsCApplicationTests { + + @Test + void contextLoads() { + } + +}