diff --git a/be/be-acp/ci-uat-acp-backend.Jenkinsfile b/be/be-acp/ci-uat-acp-backend.Jenkinsfile
new file mode 100644
index 0000000..cb89f8a
--- /dev/null
+++ b/be/be-acp/ci-uat-acp-backend.Jenkinsfile
@@ -0,0 +1,392 @@
+pipeline {
+ agent any
+ options { disableConcurrentBuilds(abortPrevious: true) }
+ environment {
+ GIT_CREDENTIALSID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129'
+ GIT_URL = 'http://work.gct.com.vn/anhln/ACP_2025.git'
+ GIT_BRANCH_NAME = 'uat-acp'
+
+ ENV = 'uat'
+ PROJECT_NAME = 'acp'
+
+ METADATA_FILENAME = "${PROJECT_NAME}_${env.ENV}_metadata.json"
+ TRIGGER_JOB_NAME = 'CD_BE_ACP'
+
+ NUGET_CONFIG_PATH = 'NuGet.config'
+ APPLICATIONCORE_PATH = 'Packages'
+ JENKINS_BUILD_FOLDER_PATH = 'Acp.WebApi'
+ JENKINS_PUBLISH_FOLDER_PATH = "${PROJECT_NAME}-${env.ENV}-publish"
+
+ COMPRESSED_FILE_NAME = "${PROJECT_NAME}_${env.ENV}_publish.zip" // tên file nén
+ COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}"
+
+ NEXUS_CREDENTIALS = credentials('Nexus_credential')
+
+ NEXUS_REPO_URL = "https://nexus.gct.com.vn/repository/${env.ENV}-acp-backend"
+ GROUP_ID = 'vn.kinhtedothi'
+ ARTIFACT_ID = 'acp-backend-api'
+ PACKAGING = 'zip'
+ VERSION = '1.0.0' // Phiên bản cơ bản
+
+ TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC'))
+ FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh
+
+ TELEGRAM_CHAT_ID = -4678013464
+ TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN')
+ }
+
+ stages {
+ stage('Checkout') {
+ steps {
+ // Checkout mã nguồn từ Gitea
+ checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH_NAME as String)
+ }
+ }
+
+ stage('Clear Nuget caches') {
+ steps {
+ // Xóa cache của NuGet
+ clearNuGetCaches()
+ }
+ }
+
+ stage('Setup Nuget.Config') {
+ steps {
+ // Thiết lập tệp NuGet.config
+ setupNuGetConfig(env.APPLICATIONCORE_PATH as String)
+ }
+ }
+
+ stage('Restore') {
+ steps {
+ // Gọi hàm để thực hiện restore
+ restoreNuGetPackages(env.JENKINS_BUILD_FOLDER_PATH as String , env.NUGET_CONFIG_PATH as String)
+ }
+ }
+
+ stage('Clean & Build') {
+ steps {
+ script {
+ cleanProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ buildProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ }
+ }
+ }
+
+ stage('Publish') {
+ steps {
+ publishProject(env.JENKINS_BUILD_FOLDER_PATH as String, env.JENKINS_PUBLISH_FOLDER_PATH as String)
+ }
+ }
+
+ stage('Package (Compress Publish Files)') {
+ steps {
+ compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH)
+ }
+ }
+
+ stage('Upload to Nexus') {
+ steps {
+ script {
+ String groupPath = env.GROUP_ID.replace('.', '/')
+ env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}"
+ env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}"
+
+ String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}"
+
+ uploadToNexus(
+ env.NEXUS_CREDENTIALS_USR,
+ env.NEXUS_CREDENTIALS_PSW,
+ env.COMPRESSED_FILE_PATH,
+ nexusUploadUrl)
+ }
+ }
+ }
+
+ stage('Create Metadata File') {
+ steps {
+ createMetadataFile(
+ env.METADATA_FILENAME,
+ env.GROUP_ID,
+ env.ARTIFACT_ID,
+ env.VERSION,
+ env.NEXUS_URL,
+ env.NEXUS_ARTIFACT_NAME)
+ echo "metadataFileName: ${env.METADATA_FILENAME}"
+ archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false
+ }
+ }
+ }
+
+ post {
+ always {
+ echo 'Pipeline execution finished.'
+ }
+ success {
+ echo "Job '${env.JOB_NAME}' completed successfully. Attempting to trigger Job '${TRIGGER_JOB_NAME}'..."
+ script {
+ def message = "✅Build thành công : API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n${env.BUILD_URL} \n Đang tiến hành Deploy...!"
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ script {
+ try {
+ def buildResult = build job: "${TRIGGER_JOB_NAME}", parameters:[
+ string(name: 'ENV', value: env.ENV),
+ string(name: 'CI_JOB_BUILD_NUMBER', value: env.BUILD_NUMBER)
+ ],
+ propagate: false
+ if (buildResult.result != 'SUCCESS') {
+ echo "[WARNING] Job 2 failed with result: ${buildResult.result}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Failed to trigger job: ${TRIGGER_JOB_NAME}. Error: ${e.message}"
+ throw e
+ }
+ }
+ }
+ failure {
+ script {
+ def message = "❌Build thất bại: API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n Kiểm tra tại đây ${env.BUILD_URL}."
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ }
+ }
+}
+
+//Thienvv- hàm checkout git
+void checkoutFromGit(String gitUrl, String credentialsId, String branch) {
+ try {
+ echo 'Start checkoutFromGit'
+
+ checkout([$class : 'GitSCM',
+ userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]],
+ branches : [[name: "*/${branch}"]]])
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv- clear nuget caches
+void clearNuGetCaches() {
+ try {
+ // Xóa cache của NuGet
+ Integer result = sh(script: 'dotnet nuget locals all --clear', returnStatus: true)
+
+ echo "result:${result}"
+
+ // Kiểm tra trạng thái trả về
+ if (result == 0) {
+ echo 'NuGet caches cleared successfully.'
+ } else {
+ error "Failed to clear NuGet caches with status: ${result}"
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - Hàm set up file Nuget.config để restore các package dependencies
+void setupNuGetConfig(String applicationCorePath) {
+ try {
+ writeFile file: 'NuGet.config', text: """
+
+
+
+
+
+"""
+
+ // Kiểm tra xem tệp đã được ghi thành công
+ if (fileExists('NuGet.config')) {
+ echo 'NuGet.config has been created successfully.'
+ } else {
+ error 'Failed to create NuGet.config.'
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+// Thienvv - Định nghĩa hàm để restore các gói NuGet
+// Thienvv - Định nghĩa hàm để restore các gói NuGet
+void restoreNuGetPackages(String restorePath, String nugetConfigPath) {
+ echo 'Starting NuGet packages restore...'
+ try {
+ if (!fileExists(restorePath)) {
+ error "restorePath is not exist : ${restorePath}"
+ }
+ if (!fileExists(nugetConfigPath)) {
+ error "nugetConfigPath is not exist : ${nugetConfigPath}"
+ }
+
+ // Thực hiện restore các gói NuGet cần thiết
+ Integer result = sh(script: "dotnet restore ${restorePath} --configfile ${nugetConfigPath}", returnStatus: true)
+
+ // Kiểm tra kết quả trả về
+ if (result == 0) {
+ echo 'NuGet packages restored successfully.'
+ } else {
+ error "Restore NuGet packages failed with exit code: ${result}. Check NuGet config and restore path."
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - clean project dotnet
+void cleanProject(String buildPath) {
+ echo 'Start cleanProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet clean ${buildPath}", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Clean Project successfully'
+ } else {
+ error 'Clean Projectfailed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - build project dotnet
+void buildProject(String buildPath) {
+ echo 'Start buildProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+ Integer result = sh(script: "dotnet build ${buildPath} -c Release", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Build Project successfully'
+ } else {
+ error 'Build Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - package/publish project dotnet
+void publishProject(String buildPath, String publishFolderPath) {
+ echo 'Start publishProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet publish ${buildPath} -c Release -o ${publishFolderPath}",
+ returnStatus: true)
+
+ echo "result : ${result}"
+
+ if (result == 0) {
+ echo 'Publish Project successfully'
+ } else {
+ error 'Publish Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - nén file
+void compressItems(String compressedFilePath, String parentFolderPath) {
+ echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}"
+ try {
+ if (fileExists(compressedFilePath)) {
+ echo "Old ZIP file exists. Deleting: ${compressedFilePath}"
+ sh "rm -f ${compressedFilePath}"
+ } else {
+ echo 'No old ZIP file found.'
+ }
+
+ if (!fileExists(parentFolderPath)) {
+ error "parentFolderPath is not exist : ${parentFolderPath}"
+ }
+
+ dir(parentFolderPath) {
+ // Thực hiện lệnh zip để nén tất cả các file trong thư mục
+ Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true)
+
+ // Kiểm tra kết quả
+ if (result == 0) {
+ echo "Compression completed successfully: ${compressedFilePath}"
+ } else {
+ error "Compression failed with exit code: ${result}."
+ }
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) {
+ echo "Starting upload ${compressedFilePath} To Nexus"
+ try {
+ // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log
+ String result = sh(script: """
+ curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}'
+ """, returnStdout: true).trim()
+
+ if (result == '200' || result == '201') {
+ echo 'uploadToNexus successfully'
+ } else {
+ error "uploadToNexus failed, HTTP status: ${result}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+def createMetadataFile(
+ String metadataFileName,
+ String groupId,
+ String artifactId,
+ String version,
+ String nexusUrl,
+ String nexusArtifactName) {
+ echo 'Starting create metadata file'
+ try {
+ // Tạo metadata
+ metadata = [
+ groupId: groupId,
+ artifactId: artifactId,
+ version: version,
+ nexusUrl: nexusUrl,
+ nexusArtifactName: nexusArtifactName
+ ]
+
+ // Ghi metadata vào file JSON
+ writeJSON file: "${metadataFileName}", json: metadata
+
+ if (!fileExists(metadataFileName)) {
+ error "metadataFileName is not exist : ${metadataFileName}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+ }
diff --git a/be/be-portal/ci-uat-portal-backend.Jenkinsfile b/be/be-portal/ci-uat-portal-backend.Jenkinsfile
new file mode 100644
index 0000000..8cdaaa4
--- /dev/null
+++ b/be/be-portal/ci-uat-portal-backend.Jenkinsfile
@@ -0,0 +1,391 @@
+pipeline {
+ agent any
+ options { disableConcurrentBuilds(abortPrevious: true) }
+ environment {
+ GIT_CREDENTIALSID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129'
+ GIT_URL = 'http://work.gct.com.vn/anhln/ACP_2025.git'
+ GIT_BRANCH_NAME = 'uat-portal'
+
+ ENV = 'uat'
+ PROJECT_NAME = 'portal'
+
+ METADATA_FILENAME = "${PROJECT_NAME}_${env.ENV}_metadata.json"
+ TRIGGER_JOB_NAME = 'CD_BE_PORTAL'
+
+ NUGET_CONFIG_PATH = 'NuGet.config'
+ APPLICATIONCORE_PATH = 'Packages'
+ JENKINS_BUILD_FOLDER_PATH = 'Portal.WebApi'
+ JENKINS_PUBLISH_FOLDER_PATH = "${PROJECT_NAME}-${env.ENV}-publish"
+
+ COMPRESSED_FILE_NAME = "${PROJECT_NAME}_${env.ENV}_publish.zip" // tên file nén
+ COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}"
+
+ NEXUS_CREDENTIALS = credentials('Nexus_credential')
+
+ NEXUS_REPO_URL = "https://nexus.gct.com.vn/repository/${env.ENV}-portal-backend"
+ GROUP_ID = 'vn.kinhtedothi'
+ ARTIFACT_ID = 'portal-backend-api'
+ PACKAGING = 'zip'
+ VERSION = '1.0.0' // Phiên bản cơ bản
+
+ TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC'))
+ FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh
+
+ TELEGRAM_CHAT_ID = -4678013464
+ TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN')
+ }
+
+ stages {
+ stage('Checkout') {
+ steps {
+ // Checkout mã nguồn từ Gitea
+ checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH_NAME as String)
+ }
+ }
+
+ stage('Clear Nuget caches') {
+ steps {
+ // Xóa cache của NuGet
+ clearNuGetCaches()
+ }
+ }
+
+ stage('Setup Nuget.Config') {
+ steps {
+ // Thiết lập tệp NuGet.config
+ setupNuGetConfig(env.APPLICATIONCORE_PATH as String)
+ }
+ }
+
+ stage('Restore') {
+ steps {
+ // Gọi hàm để thực hiện restore
+ restoreNuGetPackages(env.JENKINS_BUILD_FOLDER_PATH as String , env.NUGET_CONFIG_PATH as String)
+ }
+ }
+
+ stage('Clean & Build') {
+ steps {
+ script {
+ cleanProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ buildProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ }
+ }
+ }
+
+ stage('Publish') {
+ steps {
+ publishProject(env.JENKINS_BUILD_FOLDER_PATH as String, env.JENKINS_PUBLISH_FOLDER_PATH as String)
+ }
+ }
+
+ stage('Package (Compress Publish Files)') {
+ steps {
+ compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH)
+ }
+ }
+
+ stage('Upload to Nexus') {
+ steps {
+ script {
+ String groupPath = env.GROUP_ID.replace('.', '/')
+ env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}"
+ env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}"
+
+ String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}"
+
+ uploadToNexus(
+ env.NEXUS_CREDENTIALS_USR,
+ env.NEXUS_CREDENTIALS_PSW,
+ env.COMPRESSED_FILE_PATH,
+ nexusUploadUrl)
+ }
+ }
+ }
+
+ stage('Create Metadata File') {
+ steps {
+ createMetadataFile(
+ env.METADATA_FILENAME,
+ env.GROUP_ID,
+ env.ARTIFACT_ID,
+ env.VERSION,
+ env.NEXUS_URL,
+ env.NEXUS_ARTIFACT_NAME)
+
+ echo "metadataFileName: ${env.METADATA_FILENAME}"
+ archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false
+ }
+ }
+ }
+
+ post {
+ always {
+ echo 'Pipeline execution finished.'
+ }
+ success {
+ echo "Job '${env.JOB_NAME}' completed successfully. Attempting to trigger Job '${TRIGGER_JOB_NAME}'..."
+ script {
+ def message = "✅Build thành công : API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n${env.BUILD_URL} \n Đang tiến hành Deploy...!"
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ script {
+ try {
+ build job: "${TRIGGER_JOB_NAME}", parameters:[
+ string(name: 'ENV', value: env.ENV),
+ string(name: 'CI_JOB_BUILD_NUMBER', value: env.BUILD_NUMBER)
+ ], propagate: false
+ }
+ catch (Exception e) {
+ echo "[ERROR] Failed to trigger job: ${TRIGGER_JOB_NAME}. Error: ${e.message}"
+ throw e
+ }
+ }
+ }
+
+ failure {
+ failure {
+ script {
+ def message = "❌Build thất bại: API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n Kiểm tra tại đây ${env.BUILD_URL}."
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ }
+ }
+ }
+}
+
+//Thienvv- hàm checkout git
+void checkoutFromGit(String gitUrl, String credentialsId, String branch) {
+ try {
+ echo 'Start checkoutFromGit'
+
+ checkout([$class : 'GitSCM',
+ userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]],
+ branches : [[name: "*/${branch}"]]])
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv- clear nuget caches
+void clearNuGetCaches() {
+ try {
+ // Xóa cache của NuGet
+ Integer result = sh(script: 'dotnet nuget locals all --clear', returnStatus: true)
+
+ echo "result:${result}"
+
+ // Kiểm tra trạng thái trả về
+ if (result == 0) {
+ echo 'NuGet caches cleared successfully.'
+ } else {
+ error "Failed to clear NuGet caches with status: ${result}"
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - Hàm set up file Nuget.config để restore các package dependencies
+void setupNuGetConfig(String applicationCorePath) {
+ try {
+ writeFile file: 'NuGet.config', text: """
+
+
+
+
+
+"""
+
+ // Kiểm tra xem tệp đã được ghi thành công
+ if (fileExists('NuGet.config')) {
+ echo 'NuGet.config has been created successfully.'
+ } else {
+ error 'Failed to create NuGet.config.'
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+// Thienvv - Định nghĩa hàm để restore các gói NuGet
+void restoreNuGetPackages(String restorePath, String nugetConfigPath) {
+ echo 'Starting NuGet packages restore...'
+ try {
+ if (!fileExists(restorePath)) {
+ error "restorePath is not exist : ${restorePath}"
+ }
+ if (!fileExists(nugetConfigPath)) {
+ error "nugetConfigPath is not exist : ${nugetConfigPath}"
+ }
+
+ // Thực hiện restore các gói NuGet cần thiết
+ Integer result = sh(script: "dotnet restore ${restorePath} --configfile ${nugetConfigPath}", returnStatus: true)
+
+ // Kiểm tra kết quả trả về
+ if (result == 0) {
+ echo 'NuGet packages restored successfully.'
+ } else {
+ error "Restore NuGet packages failed with exit code: ${result}. Check NuGet config and restore path."
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - clean project dotnet
+void cleanProject(String buildPath) {
+ echo 'Start cleanProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet clean ${buildPath}", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Clean Project successfully'
+ } else {
+ error 'Clean Projectfailed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - build project dotnet
+void buildProject(String buildPath) {
+ echo 'Start buildProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+ Integer result = sh(script: "dotnet build ${buildPath} -c Release", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Build Project successfully'
+ } else {
+ error 'Build Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - package/publish project dotnet
+void publishProject(String buildPath, String publishFolderPath) {
+ echo 'Start publishProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet publish ${buildPath} -c Release -o ${publishFolderPath}",
+ returnStatus: true)
+
+ echo "result : ${result}"
+
+ if (result == 0) {
+ echo 'Publish Project successfully'
+ } else {
+ error 'Publish Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - nén file
+void compressItems(String compressedFilePath, String parentFolderPath) {
+ echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}"
+ try {
+ if (fileExists(compressedFilePath)) {
+ echo "Old ZIP file exists. Deleting: ${compressedFilePath}"
+ sh "rm -f ${compressedFilePath}"
+ } else {
+ echo 'No old ZIP file found.'
+ }
+
+ if (!fileExists(parentFolderPath)) {
+ error "parentFolderPath is not exist : ${parentFolderPath}"
+ }
+
+ dir(parentFolderPath) {
+ // Thực hiện lệnh zip để nén tất cả các file trong thư mục
+ Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true)
+
+ // Kiểm tra kết quả
+ if (result == 0) {
+ echo "Compression completed successfully: ${compressedFilePath}"
+ } else {
+ error "Compression failed with exit code: ${result}."
+ }
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) {
+ echo "Starting upload ${compressedFilePath} To Nexus"
+ try {
+ // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log
+ String result = sh(script: """
+ curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}'
+ """, returnStdout: true).trim()
+
+ if (result == '200' || result == '201') {
+ echo 'uploadToNexus successfully'
+ } else {
+ error "uploadToNexus failed, HTTP status: ${result}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+def createMetadataFile(
+ String metadataFileName,
+ String groupId,
+ String artifactId,
+ String version,
+ String nexusUrl,
+ String nexusArtifactName) {
+ echo 'Starting create metadata file'
+ try {
+ // Tạo metadata
+ metadata = [
+ groupId: groupId,
+ artifactId: artifactId,
+ version: version,
+ nexusUrl: nexusUrl,
+ nexusArtifactName: nexusArtifactName
+ ]
+
+ // Ghi metadata vào file JSON
+ writeJSON file: "${metadataFileName}", json: metadata
+
+ if (!fileExists(metadataFileName)) {
+ error "metadataFileName is not exist : ${metadataFileName}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+ }
diff --git a/be/be-resource/ci-uat-resource-backend.Jenkinsfile b/be/be-resource/ci-uat-resource-backend.Jenkinsfile
new file mode 100644
index 0000000..6dd534b
--- /dev/null
+++ b/be/be-resource/ci-uat-resource-backend.Jenkinsfile
@@ -0,0 +1,392 @@
+pipeline {
+ agent any
+ options { disableConcurrentBuilds(abortPrevious: true) }
+ environment {
+ GIT_CREDENTIALSID = 'd3de261f-8f1e-470b-b6d1-2fb4965e0129'
+ GIT_URL = 'http://work.gct.com.vn/anhln/ACP_2025.git'
+ GIT_BRANCH_NAME = 'uat-resource'
+
+ ENV = 'uat'
+ PROJECT_NAME = 'resource'
+
+ METADATA_FILENAME = "${PROJECT_NAME}_${env.ENV}_metadata.json"
+ TRIGGER_JOB_NAME = 'CD-BE-RESOURCE'
+
+ NUGET_CONFIG_PATH = 'NuGet.config'
+ APPLICATIONCORE_PATH = 'Packages'
+ JENKINS_BUILD_FOLDER_PATH = 'Resource.WebApi'
+ JENKINS_PUBLISH_FOLDER_PATH = "${PROJECT_NAME}-${env.ENV}-publish"
+
+ COMPRESSED_FILE_NAME = "${PROJECT_NAME}_${env.ENV}_publish.zip" // tên file nén
+ COMPRESSED_FILE_PATH = "${env.WORKSPACE }/${COMPRESSED_FILE_NAME}"
+
+ NEXUS_CREDENTIALS = credentials('Nexus_credential')
+
+ NEXUS_REPO_URL = "https://nexus.gct.com.vn/repository/${env.ENV}-resource-backend"
+ GROUP_ID = 'vn.kinhtedothi'
+ ARTIFACT_ID = 'resource-backend-api'
+ PACKAGING = 'zip'
+ VERSION = '1.0.0' // Phiên bản cơ bản
+
+ TIMESTAMP = new Date().format('yyyyMMdd.HH', TimeZone.getTimeZone('UTC'))
+ FULL_VERSION = "${env.VERSION}-${env.TIMESTAMP}-${env.BUILD_NUMBER}" // Tạo phiên bản hoàn chỉnh
+
+ TELEGRAM_CHAT_ID = -4678013464
+ TELEGRAM_BOT_TOKEN = credentials('TELEGRAM_BOT_TOKEN')
+ }
+
+ stages {
+ stage('Checkout') {
+ steps {
+ // Checkout mã nguồn từ Gitea
+ checkoutFromGit(env.GIT_URL as String, env.GIT_CREDENTIALSID as String, env.GIT_BRANCH_NAME as String)
+ }
+ }
+
+ stage('Clear Nuget caches') {
+ steps {
+ // Xóa cache của NuGet
+ clearNuGetCaches()
+ }
+ }
+
+ stage('Setup Nuget.Config') {
+ steps {
+ // Thiết lập tệp NuGet.config
+ setupNuGetConfig(env.APPLICATIONCORE_PATH as String)
+ }
+ }
+
+ stage('Restore') {
+ steps {
+ // Gọi hàm để thực hiện restore
+ restoreNuGetPackages(env.JENKINS_BUILD_FOLDER_PATH as String , env.NUGET_CONFIG_PATH as String)
+ }
+ }
+
+ stage('Clean & Build') {
+ steps {
+ script {
+ cleanProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ buildProject(env.JENKINS_BUILD_FOLDER_PATH as String)
+ }
+ }
+ }
+
+ stage('Publish') {
+ steps {
+ publishProject(env.JENKINS_BUILD_FOLDER_PATH as String, env.JENKINS_PUBLISH_FOLDER_PATH as String)
+ }
+ }
+
+ stage('Package (Compress Publish Files)') {
+ steps {
+ compressItems(env.COMPRESSED_FILE_PATH, env.JENKINS_PUBLISH_FOLDER_PATH)
+ }
+ }
+
+ stage('Upload to Nexus') {
+ steps {
+ script {
+ String groupPath = env.GROUP_ID.replace('.', '/')
+ env.NEXUS_URL = "${env.NEXUS_REPO_URL}/${groupPath}/${env.ARTIFACT_ID}/${env.VERSION}"
+ env.NEXUS_ARTIFACT_NAME = "${env.ARTIFACT_ID}-${env.FULL_VERSION}.${env.PACKAGING}"
+
+ String nexusUploadUrl = "${env.NEXUS_URL}/${env.NEXUS_ARTIFACT_NAME}"
+
+ uploadToNexus(
+ env.NEXUS_CREDENTIALS_USR,
+ env.NEXUS_CREDENTIALS_PSW,
+ env.COMPRESSED_FILE_PATH,
+ nexusUploadUrl)
+ }
+ }
+ }
+
+ stage('Create Metadata File') {
+ steps {
+ createMetadataFile(
+ env.METADATA_FILENAME,
+ env.GROUP_ID,
+ env.ARTIFACT_ID,
+ env.VERSION,
+ env.NEXUS_URL,
+ env.NEXUS_ARTIFACT_NAME)
+ echo "metadataFileName: ${env.METADATA_FILENAME}"
+ archiveArtifacts artifacts: "${env.METADATA_FILENAME}", allowEmptyArchive: false
+ }
+ }
+ }
+
+ post {
+ always {
+ echo 'Pipeline execution finished.'
+ }
+ success {
+ echo "Job '${env.JOB_NAME}' completed successfully. Attempting to trigger Job '${TRIGGER_JOB_NAME}'..."
+ script {
+ def message = "✅Build thành công : API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n${env.BUILD_URL} \n Đang tiến hành Deploy...!"
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ script {
+ try {
+ def buildResult = build job: "${TRIGGER_JOB_NAME}", parameters:[
+ string(name: 'ENV', value: env.ENV),
+ string(name: 'CI_JOB_BUILD_NUMBER', value: env.BUILD_NUMBER)
+ ],
+ propagate: false
+ if (buildResult.result != 'SUCCESS') {
+ echo "[WARNING] Job 2 failed with result: ${buildResult.result}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Failed to trigger job: ${TRIGGER_JOB_NAME}. Error: ${e.message}"
+ throw e
+ }
+ }
+ }
+ failure {
+ script {
+ def message = "❌Build thất bại: API - Môi trường ${env.ENV} - Dự án ${env.PROJECT_NAME} \n ${currentBuild.fullDisplayName}\n Kiểm tra tại đây ${env.BUILD_URL}."
+ sh "curl -s -X POST https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage -d chat_id=${env.TELEGRAM_CHAT_ID} -d text=\"${message}\""
+ }
+ }
+ }
+}
+
+//Thienvv- hàm checkout git
+void checkoutFromGit(String gitUrl, String credentialsId, String branch) {
+ try {
+ echo 'Start checkoutFromGit'
+
+ checkout([$class : 'GitSCM',
+ userRemoteConfigs: [[url: gitUrl, credentialsId: credentialsId]],
+ branches : [[name: "*/${branch}"]]])
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv- clear nuget caches
+void clearNuGetCaches() {
+ try {
+ // Xóa cache của NuGet
+ Integer result = sh(script: 'dotnet nuget locals all --clear', returnStatus: true)
+
+ echo "result:${result}"
+
+ // Kiểm tra trạng thái trả về
+ if (result == 0) {
+ echo 'NuGet caches cleared successfully.'
+ } else {
+ error "Failed to clear NuGet caches with status: ${result}"
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - Hàm set up file Nuget.config để restore các package dependencies
+void setupNuGetConfig(String applicationCorePath) {
+ try {
+ writeFile file: 'NuGet.config', text: """
+
+
+
+
+
+"""
+
+ // Kiểm tra xem tệp đã được ghi thành công
+ if (fileExists('NuGet.config')) {
+ echo 'NuGet.config has been created successfully.'
+ } else {
+ error 'Failed to create NuGet.config.'
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+// Thienvv - Định nghĩa hàm để restore các gói NuGet
+// Thienvv - Định nghĩa hàm để restore các gói NuGet
+void restoreNuGetPackages(String restorePath, String nugetConfigPath) {
+ echo 'Starting NuGet packages restore...'
+ try {
+ if (!fileExists(restorePath)) {
+ error "restorePath is not exist : ${restorePath}"
+ }
+ if (!fileExists(nugetConfigPath)) {
+ error "nugetConfigPath is not exist : ${nugetConfigPath}"
+ }
+
+ // Thực hiện restore các gói NuGet cần thiết
+ Integer result = sh(script: "dotnet restore ${restorePath} --configfile ${nugetConfigPath}", returnStatus: true)
+
+ // Kiểm tra kết quả trả về
+ if (result == 0) {
+ echo 'NuGet packages restored successfully.'
+ } else {
+ error "Restore NuGet packages failed with exit code: ${result}. Check NuGet config and restore path."
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - clean project dotnet
+void cleanProject(String buildPath) {
+ echo 'Start cleanProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet clean ${buildPath}", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Clean Project successfully'
+ } else {
+ error 'Clean Projectfailed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - build project dotnet
+void buildProject(String buildPath) {
+ echo 'Start buildProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+ Integer result = sh(script: "dotnet build ${buildPath} -c Release", returnStatus: true)
+
+ echo "result:${result}"
+
+ if (result == 0) {
+ echo 'Build Project successfully'
+ } else {
+ error 'Build Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - package/publish project dotnet
+void publishProject(String buildPath, String publishFolderPath) {
+ echo 'Start publishProject'
+ try {
+ if (!fileExists(buildPath)) {
+ error "buildPath is not exist : ${buildPath}"
+ }
+
+ Integer result = sh(script: "dotnet publish ${buildPath} -c Release -o ${publishFolderPath}",
+ returnStatus: true)
+
+ echo "result : ${result}"
+
+ if (result == 0) {
+ echo 'Publish Project successfully'
+ } else {
+ error 'Publish Project failed'
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+//Thienvv - nén file
+void compressItems(String compressedFilePath, String parentFolderPath) {
+ echo "Starting compression of the entire folder into a zip file : ${compressedFilePath}"
+ try {
+ if (fileExists(compressedFilePath)) {
+ echo "Old ZIP file exists. Deleting: ${compressedFilePath}"
+ sh "rm -f ${compressedFilePath}"
+ } else {
+ echo 'No old ZIP file found.'
+ }
+
+ if (!fileExists(parentFolderPath)) {
+ error "parentFolderPath is not exist : ${parentFolderPath}"
+ }
+
+ dir(parentFolderPath) {
+ // Thực hiện lệnh zip để nén tất cả các file trong thư mục
+ Integer result = sh(script: "zip -r ${compressedFilePath} * ", returnStatus: true)
+
+ // Kiểm tra kết quả
+ if (result == 0) {
+ echo "Compression completed successfully: ${compressedFilePath}"
+ } else {
+ error "Compression failed with exit code: ${result}."
+ }
+ }
+ } catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+void uploadToNexus(String nexusUsername, String nexusPassword, String compressedFilePath, String uploadUrl) {
+ echo "Starting upload ${compressedFilePath} To Nexus"
+ try {
+ // Truyền các biến môi trường vào lệnh sh mà không lộ ra trong log
+ String result = sh(script: """
+ curl -u ${nexusUsername}:${nexusPassword} --upload-file ${compressedFilePath} ${uploadUrl} -w '%{http_code}'
+ """, returnStdout: true).trim()
+
+ if (result == '200' || result == '201') {
+ echo 'uploadToNexus successfully'
+ } else {
+ error "uploadToNexus failed, HTTP status: ${result}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+}
+
+def createMetadataFile(
+ String metadataFileName,
+ String groupId,
+ String artifactId,
+ String version,
+ String nexusUrl,
+ String nexusArtifactName) {
+ echo 'Starting create metadata file'
+ try {
+ // Tạo metadata
+ metadata = [
+ groupId: groupId,
+ artifactId: artifactId,
+ version: version,
+ nexusUrl: nexusUrl,
+ nexusArtifactName: nexusArtifactName
+ ]
+
+ // Ghi metadata vào file JSON
+ writeJSON file: "${metadataFileName}", json: metadata
+
+ if (!fileExists(metadataFileName)) {
+ error "metadataFileName is not exist : ${metadataFileName}"
+ }
+ }
+ catch (Exception e) {
+ echo "[ERROR] Unexpected error: ${e.message}"
+ throw e
+ }
+ }