Browse Source

initbranch

fengchang 1 year ago
commit
584fdcaee0
100 changed files with 4043 additions and 0 deletions
  1. 65 0
      Caddyfile
  2. 2 0
      README.md
  3. 37 0
      hichina-admin-backend/.gitignore
  4. 53 0
      hichina-admin-backend/build.gradle
  5. BIN
      hichina-admin-backend/gradle/wrapper/gradle-wrapper.jar
  6. 5 0
      hichina-admin-backend/gradle/wrapper/gradle-wrapper.properties
  7. 240 0
      hichina-admin-backend/gradlew
  8. 91 0
      hichina-admin-backend/gradlew.bat
  9. 1 0
      hichina-admin-backend/settings.gradle
  10. 34 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/HichinaAdminBackendApplication.java
  11. 23 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/Constants.java
  12. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomAuthenticationFailureHandler.java
  13. 41 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomAuthenticationProvider.java
  14. 22 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomLogoutSuccessHandler.java
  15. 75 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/MyWebSecurityConfiguration.java
  16. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RESTAuthenticationEntryPoint.java
  17. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RESTAuthenticationSuccessHandler.java
  18. 80 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RequestFilter.java
  19. 67 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/AdminUserController.java
  20. 26 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/DemoController.java
  21. 139 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/DestinationController.java
  22. 127 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/GuidebookController.java
  23. 128 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/HichinaLineController.java
  24. 51 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ImageController.java
  25. 69 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/LoginController.java
  26. 75 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/OrderController.java
  27. 94 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/PageContentAdminController.java
  28. 83 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductSharedAttributesController.java
  29. 363 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductSkuController.java
  30. 138 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductTypeController.java
  31. 19 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/PublicController.java
  32. 105 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/SkuGroupController.java
  33. 24 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/UserController.java
  34. 32 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/AdminUserMapper.java
  35. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/BlogMapper.java
  36. 52 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/DestinationMapper.java
  37. 45 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/GuidebookMapper.java
  38. 46 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/HichinaLineMapper.java
  39. 68 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/HichinaProductMapper.java
  40. 32 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/OrderMapper.java
  41. 35 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductAttributeMapper.java
  42. 82 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuGroupMapper.java
  43. 34 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuIntAttributeMappingMapper.java
  44. 35 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuTimestampAttributeMappingMapper.java
  45. 33 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuVarcharAttributeMappingMapper.java
  46. 49 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductTypeMapper.java
  47. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/UserMapper.java
  48. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/AdminUser.java
  49. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/AdminUserCreateDTO.java
  50. 11 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/BindAtrribute2ProductRequest.java
  51. 16 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/CreateGuidebookDTO.java
  52. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationBatchDeleteRequest.java
  53. 18 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationCreateDTO.java
  54. 18 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationUpdateDTO.java
  55. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GeneralBatchDeleteRequest.java
  56. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GuidebookDeleteRequest.java
  57. 20 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GuidebookListDTO.java
  58. 27 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaProductDTO.java
  59. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaProductWithPropertyBagDTO.java
  60. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaResponse.java
  61. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LineBatchDeleteRequest.java
  62. 19 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LineRequest.java
  63. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LoginTestDTO.java
  64. 27 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/OrderDetailDTO.java
  65. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/OrderListDTO.java
  66. 22 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/PaginationWrapper.java
  67. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeBatchDeleteRequest.java
  68. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeCreateDTO.java
  69. 14 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeTypeShortDTO.java
  70. 20 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductPropertyBag.java
  71. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuBatchDeleteRequest.java
  72. 20 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuCreateDTO.java
  73. 17 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuUpdateDTO.java
  74. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductTypeBatchDeleteRequestDTO.java
  75. 13 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductTypeCreateDTO.java
  76. 23 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/SkuGroupStatsDTO.java
  77. 27 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Destination.java
  78. 22 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Guidebook.java
  79. 35 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/HichinaLine.java
  80. 25 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/HichinaProduct.java
  81. 28 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Order.java
  82. 19 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductAttribute.java
  83. 22 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuGroup.java
  84. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuIntAttributeMapping.java
  85. 23 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuTimestampAttributeMapping.java
  86. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuVarcharAttributeMapping.java
  87. 21 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductType.java
  88. 19 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductTypeAttributeMapping.java
  89. 33 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/User.java
  90. 17 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/BlogSlideImage.java
  91. 15 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/GuidebookIntroSlideImage.java
  92. 17 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/HomeSlideImage.java
  93. 12 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/BlogSlideImageRepository.java
  94. 7 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/GuidebookIntroSlideImageRepository.java
  95. 15 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/HomeSlideImageRepository.java
  96. 32 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/schedule/ScheduledJobs.java
  97. 164 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/service/InitDataService.java
  98. 26 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/AtomicSequenceGenerator.java
  99. 5 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/SequenceGenerator.java
  100. 36 0
      hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/UserUtil.java

+ 65 - 0
Caddyfile

@@ -0,0 +1,65 @@
+:9051 {
+	root * /root/Downloads/hichina2023/hichina-admin-front/dist/spa
+	try_files {path} /index.html
+	file_server
+	log {
+		output file ./hichinaadminfront.log {
+			roll_size 1gb
+			roll_keep 5
+			roll_keep_for 720h
+		}
+	}
+	handle_errors {
+		rewrite * /404.html
+		file_server
+	}
+}
+
+:8765 {
+	root * /root/underconstruction/dist/spa
+	try_files {path} /index.html
+	file_server
+	log {
+		output file ./underconstruction.log {
+			roll_size 1gb
+			roll_keep 5
+			roll_keep_for 720h
+		}
+	}
+	handle_errors {
+		rewrite * /404.html
+		file_server
+	}
+}
+:9053 {
+	root * /root/Downloads/hichina2023/hichina-main-front/dist
+	try_files {path} /index.html
+	file_server
+	log {
+		output file ./mainsiteqafront.log {
+			roll_size 1gb
+			roll_keep 5
+			roll_keep_for 720h
+		}
+	}
+	handle_errors {
+		rewrite * /404.html
+		file_server
+	}
+}
+:8011 {
+	encode gzip
+	root * /root/Downloads/staticresource
+	file_server
+	log {
+		output file ./staticresource.log {
+			roll_size 1gb
+			roll_keep 5
+			roll_keep_for 720h
+		}
+	}
+	handle_errors {
+		rewrite * /404.html
+		file_server
+	}
+}

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+# 转移到private git server gogs上 
+

+ 37 - 0
hichina-admin-backend/.gitignore

@@ -0,0 +1,37 @@
+HELP.md
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/

+ 53 - 0
hichina-admin-backend/build.gradle

@@ -0,0 +1,53 @@
+plugins {
+	id 'java'
+	id 'org.springframework.boot' version '3.0.2'
+	id 'io.spring.dependency-management' version '1.1.0'
+}
+
+group = 'com.hichina.admin'
+version = '0.0.1-SNAPSHOT'
+sourceCompatibility = '17'
+
+repositories {
+	maven { url "https://maven.aliyun.com/repository/public" }
+}
+
+
+dependencies {
+	implementation 'org.springframework.boot:spring-boot-starter-web'
+	implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
+	implementation 'org.projectlombok:lombok:1.18.20'
+	testImplementation 'org.springframework.boot:spring-boot-starter-test'
+	// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security
+	implementation 'org.springframework.boot:spring-boot-starter-security:3.0.2'
+	developmentOnly 'org.springframework.boot:spring-boot-devtools'
+	// https://mvnrepository.com/artifact/org.liquibase/liquibase-core
+	implementation 'org.liquibase:liquibase-core:4.19.0'
+	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+	// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
+	implementation 'com.mysql:mysql-connector-j:8.0.32'
+	// https://mvnrepository.com/artifact/org.projectlombok/lombok
+	compileOnly 'org.projectlombok:lombok:1.18.26'
+	annotationProcessor 'org.projectlombok:lombok:1.18.26'
+	testCompileOnly 'org.projectlombok:lombok:1.18.26'
+	testAnnotationProcessor 'org.projectlombok:lombok:1.18.26'
+	// https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter
+	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.1'
+	// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
+	implementation 'org.springframework.boot:spring-boot-starter-validation:3.0.2'
+	implementation 'com.aliyun.oss:aliyun-sdk-oss:3.16.1'
+	// https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core
+	implementation 'com.aliyun:aliyun-java-sdk-core:4.6.3'
+	// https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-dysmsapi
+	implementation 'com.aliyun:aliyun-java-sdk-dysmsapi:2.2.1'
+	// https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-sts
+	implementation 'com.aliyun:aliyun-java-sdk-sts:3.1.0'
+	// https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter
+	implementation 'com.github.pagehelper:pagehelper-spring-boot-starter:1.4.6'
+	// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-logging
+	implementation 'org.springframework.boot:spring-boot-starter-logging:3.0.3'
+}
+
+tasks.named('test') {
+	useJUnitPlatform()
+}

BIN
hichina-admin-backend/gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
hichina-admin-backend/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 240 - 0
hichina-admin-backend/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" "$@"

+ 91 - 0
hichina-admin-backend/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

+ 1 - 0
hichina-admin-backend/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'hichina-admin-backend'

+ 34 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/HichinaAdminBackendApplication.java

@@ -0,0 +1,34 @@
+package com.hichina.admin.hichinaadminbackend;
+
+import com.hichina.admin.hichinaadminbackend.service.InitDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableMongoRepositories
+@EnableScheduling
+public class HichinaAdminBackendApplication {
+
+	@Autowired
+	private InitDataService initDataService;
+
+	public static void main(String[] args) {
+		SpringApplication.run(HichinaAdminBackendApplication.class, args);
+	}
+
+	@Bean
+	public CommandLineRunner CommandLineRunnerBean() {
+		return (args) -> {
+			initDataService.initAdminUser();
+			initDataService.initProperty();
+			initDataService.initProductType();
+			initDataService.initProductTypeBinding();
+		};
+	}
+
+}

+ 23 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/Constants.java

@@ -0,0 +1,23 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+public class Constants {
+    public static String PRODUCTPROFILEIMAGEPROP="2dea54f4-9b9c-413a-8b3a-0caf273283d2";
+    public static String AVAILABLEDATEGUIDPROP="f0b807e5-d1a6-4454-a400-7905a4fea492";
+    public static String ENDDATEGUIDPROP="cde36fa1-ab1d-4d17-a44f-a7433c2fb264";
+    public static String DAYSPROP="8cc865ff-b30f-4f00-b426-9e64418e5100";
+    public static String SUPPLIERPROP="04a54b65-a1f3-45d9-955c-40cf88f21b82";
+    public static String TYPEOFPACKAGEPROP="11cd8b32-c4f6-47db-8b8a-486c992bf43b";
+    public static String ADULTPRICEPROP="e228b843-e054-41f8-91dd-19663460df54";
+    public static String CHILDPRICEPROP="c4c845a7-4bef-46d8-a5ad-d72a5464e8b1";
+    public static String INFANTPRICEPROP="448406cb-b68f-439e-9da8-148d78ae8404";
+    public static String SINGLEDIFFPRICEPROP="d4825026-3ad8-4861-8e8f-e980b55fe67b";
+    public static String TOURPRODUCTTYPE="3a53caed-b788-4290-896d-7922532ad769";
+    public static String FLIGHTHOTELPRODUCTTYPE="e05d07a3-a717-45b8-b009-47a349890e41";
+    public static String FLIGHTPRODUCTTYPE="d7b95089-e278-4522-8f71-dacac41ba32f";
+    public static String HOTELPRODUCTTYPE="a9f5adbe-c09b-49bc-a614-8a1c5d5e5337";
+    public static String LOCALSPECIALTYPRODUCTTYPE="fd264cab-ee8d-4571-a477-03d7e7c090b3";
+
+    public static String GENERALPRICE="e16df480-b17d-4442-91c2-d6c30d0d7ab0";
+    public static String MAXNUM="720f4f2e-e114-4003-9806-bc56a9366278";
+
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomAuthenticationFailureHandler.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+
+import java.io.IOException;
+import java.util.Calendar;
+
+public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
+
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
+        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
+
+        String jsonPayload = "{\"message\" : \"%s\", \"timestamp\" : \"%s\" }";
+        httpServletResponse.getOutputStream().println(String.format(jsonPayload, e.getMessage(), Calendar.getInstance().getTime()));
+    }
+}

+ 41 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomAuthenticationProvider.java

@@ -0,0 +1,41 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import com.hichina.admin.hichinaadminbackend.mapper.AdminUserMapper;
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.stereotype.Component;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class CustomAuthenticationProvider implements AuthenticationProvider {
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+
+    @Override
+    public Authentication authenticate(Authentication authentication)
+            throws AuthenticationException {
+        String name = authentication.getName();
+        String password = authentication.getCredentials().toString();
+
+        List<AdminUser> users = adminUserMapper.getUserByName(name);
+        if(users.isEmpty()){
+            return null;
+        }
+        AdminUser user = users.get(0);
+        if(password.equals(user.getPassword())){
+            return new UsernamePasswordAuthenticationToken(user.getUsername(), password, new ArrayList<>());
+        }else{
+            return null;
+        }
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication) {
+        return authentication.equals(UsernamePasswordAuthenticationToken.class);
+    }
+}

+ 22 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/CustomLogoutSuccessHandler.java

@@ -0,0 +1,22 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+
+import java.io.IOException;
+
+public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
+
+    @Override
+    public void onLogoutSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException, ServletException {
+        final String refererUrl = request.getHeader("Referer");
+        System.out.println(refererUrl);
+
+        super.onLogoutSuccess(request, response, authentication);
+    }
+
+}

+ 75 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/MyWebSecurityConfiguration.java

@@ -0,0 +1,75 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+@Configuration
+@EnableWebSecurity
+public class MyWebSecurityConfiguration {
+    @Bean
+    public WebSecurityCustomizer webSecurityCustomizer() {
+        return (web) -> web.ignoring()
+                // Spring Security should completely ignore URLs starting with /resources/
+                .requestMatchers("/resources/**");
+    }
+
+
+    @Autowired
+    private CustomAuthenticationProvider authProvider;
+
+    @Bean
+    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
+        AuthenticationManagerBuilder authenticationManagerBuilder =
+                http.getSharedObject(AuthenticationManagerBuilder.class);
+        authenticationManagerBuilder.authenticationProvider(authProvider);
+        return authenticationManagerBuilder.build();
+    }
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http.csrf().disable().authorizeRequests().requestMatchers("/api/v1/**").authenticated();
+        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());
+        http.formLogin().successHandler(loginSuccessHandler());
+        http.formLogin().failureHandler(authenticationFailureHandler());
+        http.logout().logoutSuccessUrl("/auth/login");
+        // .and()
+        // .exceptionHandling().accessDeniedPage("/accessDenied");
+        // .exceptionHandling().accessDeniedHandler(accessDeniedHandler());
+        http.cors();
+        return http.build();
+    }
+
+    @Bean
+    public AuthenticationFailureHandler authenticationFailureHandler() {
+        return new CustomAuthenticationFailureHandler();
+    }
+
+    @Bean
+    public RESTAuthenticationEntryPoint authenticationEntryPoint(){
+        return new RESTAuthenticationEntryPoint();
+    }
+
+    @Bean
+    public LogoutSuccessHandler logoutSuccessHandler() {
+        return new CustomLogoutSuccessHandler();
+    }
+
+    @Bean RESTAuthenticationSuccessHandler loginSuccessHandler(){
+        return new RESTAuthenticationSuccessHandler();
+    }
+
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RESTAuthenticationEntryPoint.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
+            throws IOException, ServletException {
+
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+    }
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RESTAuthenticationSuccessHandler.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
+                                        Authentication authentication) throws IOException, ServletException {
+
+        clearAuthenticationAttributes(request);
+    }
+}

+ 80 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/config/RequestFilter.java

@@ -0,0 +1,80 @@
+package com.hichina.admin.hichinaadminbackend.config;
+
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * Servlet Filter implementation class CORSFilter
+ */
+// Enable it for Servlet 3.x implementations
+/* @ WebFilter(asyncSupported = true, urlPatterns = { "/*" }) */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class RequestFilter implements Filter {
+
+    @Autowired
+    private Environment env;
+
+    /**
+     * Default constructor.
+     */
+    public RequestFilter() {
+        // TODO Auto-generated constructor stub
+    }
+
+    /**
+     * @see Filter#destroy()
+     */
+    @Override
+    public void destroy() {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
+     */
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
+            throws IOException, ServletException {
+
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        System.out.println("CORSFilter HTTP Request: " + request.getMethod());
+
+        String originBase = env.getProperty("origin.base");
+
+        // Authorize (allow) all domains to consume the content
+        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", originBase);
+        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST, DELETE");
+        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Headers","authorization, Access-Control-Allow-Origin, content-type, hash-referer,  x-auth-token, cache-control, " +
+                "access-control-request-headers,access-control-request-method,accept,origin,authorization,x-requested-with");
+        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Credentials","true");
+        ((HttpServletResponse) servletResponse).addHeader("Access-Control-Expose-Headers","Access-Control-Allow-Origin");
+        HttpServletResponse resp = (HttpServletResponse) servletResponse;
+
+        // For HTTP OPTIONS verb/method reply with ACCEPTED status code -- per CORS handshake
+        if (request.getMethod().equals("OPTIONS")) {
+            resp.setStatus(HttpServletResponse.SC_ACCEPTED);
+            return;
+        }
+
+        // pass the request along the filter chain
+        chain.doFilter(request, servletResponse);
+    }
+
+    /**
+     * @see Filter#init(FilterConfig)
+     */
+    @Override
+    public void init(FilterConfig fConfig) throws ServletException {
+        // TODO Auto-generated method stub
+    }
+
+}

+ 67 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/AdminUserController.java

@@ -0,0 +1,67 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.hichina.admin.hichinaadminbackend.mapper.AdminUserMapper;
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import com.hichina.admin.hichinaadminbackend.model.DTO.AdminUserCreateDTO;
+import com.hichina.admin.hichinaadminbackend.model.DTO.GeneralBatchDeleteRequest;
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/adminuser")
+public class AdminUserController {
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+
+    @DeleteMapping("/batch")
+    public HichinaResponse batchDeleteUser(@RequestBody  GeneralBatchDeleteRequest deleteRequest){
+        if(deleteRequest.getToDelete().contains("hichinaadmin")){
+            throw new RuntimeException("Cannot delete superadmin");
+        }
+        adminUserMapper.batchDelete(deleteRequest.getToDelete());
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setMessage("Succeed deleting users: "+ deleteRequest.getToDelete());
+        return ret;
+    }
+
+    @PostMapping
+    public HichinaResponse createAdminUser(@RequestBody AdminUserCreateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+        AdminUser user2Create = new AdminUser();
+        String username = request.getUsername();
+        if("1".equals(request.getRole())){
+            // EMPLOYEE
+            username = "EMP_"+username;
+        }else if("2".equals(request.getRole())){
+            // SUPPLIER
+            username = "SP_"+username;
+        }
+        user2Create.setUsername(username);
+        user2Create.setPassword(request.getPassword());
+        user2Create.setCreatedTime(new Date());
+
+        adminUserMapper.insert(user2Create);
+
+        ret.setMessage("Succeed createing user: "+ username);
+        ret.setOk(true);
+
+        return ret;
+    }
+
+    @GetMapping("/listalladminuser")
+    public HichinaResponse getAllAdminUsers(){
+        HichinaResponse ret = new HichinaResponse();
+        List<AdminUser> adminUsers = adminUserMapper.getAllAdminUser();
+
+        ret.setOk(true);
+        ret.setData(adminUsers);
+        ret.setMessage("Succeed getting all admin users");
+
+        return ret;
+    }
+}

+ 26 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/DemoController.java

@@ -0,0 +1,26 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.hichina.admin.hichinaadminbackend.repository.HomeSlideImageRepository;
+import com.hichina.admin.hichinaadminbackend.schedule.ScheduledJobs;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+@RequestMapping("/api/v1/demo")
+public class DemoController {
+    @Autowired
+    private HomeSlideImageRepository repository;
+
+    @Autowired
+    private ScheduledJobs scheduledJobs;
+
+    @GetMapping
+    public String mongotest(){
+        scheduledJobs.UpdateBlogProfileImage();
+
+        return "succeed";
+    }
+}

+ 139 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/DestinationController.java

@@ -0,0 +1,139 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.DestinationMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import com.hichina.admin.hichinaadminbackend.model.Destination;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/destination")
+public class DestinationController {
+
+    @Autowired
+    private DestinationMapper destinationMapper;
+
+    @PostMapping
+    public HichinaResponse createDestination(@RequestBody DestinationCreateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        Destination destination = new Destination();
+        destination.setDestinationId(java.util.UUID.randomUUID().toString());
+        destination.setDestinationName(request.getName());
+        destination.setDescription(request.getDescription());
+        destination.setLevel(request.getLevel());
+        destination.setParentId(request.getParentId());
+        destination.setDestinationProfileImage(request.getDestinationProfileImage());
+        destination.setCreatedDate(new Date());
+
+        destinationMapper.insert(destination);
+
+        ret.setData(destination);
+        ret.setOk(true);
+        ret.setMessage("成功新建目的地");
+
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    @Transactional
+    public HichinaResponse deleteDestinations(@RequestBody DestinationBatchDeleteRequest req){
+
+        // delete base product record
+        destinationMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除目的地");
+
+        return ret;
+    }
+
+    @PutMapping("/{destinationId}")
+    @Transactional
+    public HichinaResponse updateDestination(@PathVariable("destinationId") String destinationId ,@RequestBody DestinationUpdateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        List<Destination> destinations = destinationMapper.findDestinationById(destinationId);
+        if(destinations.isEmpty()){
+            throw new RuntimeException("Nothing to update");
+        }
+        Destination toUpdate = destinations.get(0);
+
+        toUpdate.setLevel(request.getLevel());
+        toUpdate.setDestinationName(request.getName());
+        toUpdate.setDescription(request.getDescription());
+        toUpdate.setDestinationProfileImage(request.getDestinationProfileImage());
+        toUpdate.setParentId(request.getParentId());
+
+        // update basic info
+        destinationMapper.update(toUpdate);
+
+        ret.setOk(true);
+        ret.setData(destinationId);
+        ret.setMessage("成功更新destination:"+destinationId+"的目的地");
+
+        return ret;
+    }
+
+    @GetMapping("/{destinationId}")
+    public Destination getByDestinationId(@PathVariable("destinationId") String destinationId){
+        List<Destination> destinations = destinationMapper.findDestinationById(destinationId);
+
+        if(destinations.isEmpty()){
+            throw new RuntimeException("destination not found");
+        }
+        return destinations.get(0);
+    }
+
+    @GetMapping
+    public HichinaResponse getDestinations(@RequestParam(value = "page", required = true) Integer page,
+                                                           @RequestParam(value = "pageSize", required = true) Integer size,
+                                                           @RequestParam(value = "query") String query,
+                                           @RequestParam(value = "level") Integer level){
+        HichinaResponse ret = new HichinaResponse();
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+        List<Destination> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            if(level!=null && level!=-1){
+                rawData = destinationMapper.findDestinationsByQueryAndLevel(query, level);
+                cnt = destinationMapper.countByQueryAndLevel(query, level);
+            }else{
+                rawData = destinationMapper.findDestinationsByQuery(query);
+                cnt = destinationMapper.countByQuery(query);
+            }
+        }else{
+            if(level!=null && level!=-1){
+                rawData = destinationMapper.findDestinationsByLevel(level);
+                cnt = destinationMapper.countByLevel(level);
+            }else{
+                rawData = destinationMapper.findAllDestinations();
+                cnt = destinationMapper.count();
+            }
+        }
+
+        ret.setMessage("Successfully got all destinations");
+
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+}

+ 127 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/GuidebookController.java

@@ -0,0 +1,127 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.GuidebookMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import com.hichina.admin.hichinaadminbackend.model.Guidebook;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/guidebook")
+public class GuidebookController {
+    @Autowired
+    private GuidebookMapper guidebookMapper;
+
+    @PutMapping("/{guideId}")
+    public HichinaResponse updateGuideBook(@PathVariable("guideId") String guideId, @RequestBody CreateGuidebookDTO request){
+        HichinaResponse ret = new HichinaResponse();
+        List<Guidebook> guidebooks = guidebookMapper.findRawByGuideId(guideId);
+        if(guidebooks.isEmpty()){
+            throw new RuntimeException("没找到任何可更新的攻略书"+guideId);
+        }
+        Guidebook toUpdate = guidebooks.get(0);
+
+        toUpdate.setDownloadUrl(request.getDownloadUrl());
+        toUpdate.setCoverImageUrl(request.getCoverImageUrl());
+        toUpdate.setDestinationId(request.getDestinationId());
+        toUpdate.setShortDescription(request.getShortDescription());
+
+        guidebookMapper.update(toUpdate);
+        ret.setMessage(String.format("成功更新id为%s的攻略书",guideId));
+        ret.setOk(true);
+        ret.setData(toUpdate);
+
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    @Transactional
+    public HichinaResponse deleteDestinations(@RequestBody GuidebookDeleteRequest req){
+
+        // delete base product record
+        guidebookMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除攻略书");
+
+        return ret;
+    }
+
+    @PostMapping
+    public HichinaResponse createGuideBook(@RequestBody CreateGuidebookDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        Guidebook guidebook = new Guidebook();
+        guidebook.setGuideId(java.util.UUID.randomUUID().toString());
+        guidebook.setDestinationId(request.getDestinationId());
+        guidebook.setCoverImageUrl(request.getCoverImageUrl());
+        guidebook.setShortDescription(request.getShortDescription());
+        guidebook.setDownloadUrl(request.getDownloadUrl());
+        guidebook.setCreatedDate(new Date());
+
+        guidebookMapper.insert(guidebook);
+
+        ret.setData(guidebook);
+        ret.setOk(true);
+        ret.setMessage("成功创建攻略书");
+        return ret;
+    }
+
+    @GetMapping("/{guideId}")
+    public HichinaResponse getGuideBookDTOById(@PathVariable("guideId") String guideId){
+        HichinaResponse ret = new HichinaResponse();
+        List<GuidebookListDTO> guidebooks = guidebookMapper.findGuidebookByGuideId(guideId);
+        if(guidebooks.isEmpty()){
+            ret.setOk(false);
+            ret.setMessage(String.format("没有找到任何id为:%s的攻略书",guideId));
+        }
+        GuidebookListDTO guidebook = guidebooks.get(0);
+
+        ret.setData(guidebook);
+        ret.setOk(true);
+        ret.setMessage(String.format("成功找到id为:%s的攻略书", guideId));
+
+        return ret;
+    }
+
+    @GetMapping
+    public HichinaResponse getDestinations(@RequestParam(value = "page", required = true) Integer page,
+                                           @RequestParam(value = "pageSize", required = true) Integer size,
+                                           @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+        List<GuidebookListDTO> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            rawData = guidebookMapper.findGuidebookByQuery(query);
+            cnt = guidebookMapper.countByQuery(query);
+        }else{
+            rawData = guidebookMapper.findAllGuidebook();
+            cnt = guidebookMapper.count();
+        }
+
+        ret.setMessage("Successfully got all guidebooks");
+
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+}

+ 128 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/HichinaLineController.java

@@ -0,0 +1,128 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.HichinaLineMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import com.hichina.admin.hichinaadminbackend.model.DTO.LineBatchDeleteRequest;
+import com.hichina.admin.hichinaadminbackend.model.DTO.LineRequest;
+import com.hichina.admin.hichinaadminbackend.model.DTO.PaginationWrapper;
+import com.hichina.admin.hichinaadminbackend.model.HichinaLine;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/line")
+public class HichinaLineController {
+
+    @Autowired
+    private HichinaLineMapper hichinaLineMapper;
+
+    @Autowired
+    private Environment env;
+
+    @PutMapping("/{publicId}")
+    public HichinaResponse updateLine(@PathVariable("publicId") String publicId , @RequestBody LineRequest request){
+        HichinaResponse ret = new HichinaResponse();
+        List<HichinaLine> lines = hichinaLineMapper.findLineByPublicId(publicId);
+
+        if(lines.isEmpty()){
+            throw new RuntimeException("No line found by publicId: "+ publicId);
+        }
+
+        HichinaLine toBeUpdated = lines.get(0);
+
+        toBeUpdated.setName(request.getName());
+        toBeUpdated.setDescription(request.getDescription());
+        toBeUpdated.setBasePrice(request.getBasePrice());
+        toBeUpdated.setRangeInDays(request.getRangeInDays());
+        toBeUpdated.setIconPath(request.getIconPath());
+
+        hichinaLineMapper.update(toBeUpdated);
+
+        ret.setMessage("成功Update publicId为"+publicId+"的记录");
+        ret.setData(publicId);
+        ret.setOk(true);
+
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    public HichinaResponse deleteLines(@RequestBody LineBatchDeleteRequest req){
+
+        hichinaLineMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除");
+
+        return ret;
+    }
+
+    @PostMapping
+    public HichinaResponse createLine(@RequestBody LineRequest params){
+        HichinaResponse ret = new HichinaResponse();
+
+        HichinaLine line = new HichinaLine();
+        line.setBasePrice(params.getBasePrice());
+        line.setCreatedDate(new Date());
+        line.setName(params.getName());
+        line.setDescription(params.getDescription());
+        line.setIconPath(params.getIconPath());
+        line.setRangeInDays(params.getRangeInDays());
+        line.setPublicId(java.util.UUID.randomUUID().toString());
+        hichinaLineMapper.insert(line);
+
+        ret.setOk(true);
+        ret.setData(line.getId());
+        ret.setMessage("成功新建线路");
+
+        return ret;
+    }
+
+    @GetMapping("/{publicId}")
+    public HichinaResponse getSingleLineById(@PathVariable("publicId") String publicId){
+        HichinaResponse ret = new HichinaResponse();
+        List<HichinaLine> lines = hichinaLineMapper.findLineByPublicId(publicId);
+
+        ret.setOk(true);
+        ret.setData(lines.isEmpty()?null:lines.get(0));
+        ret.setMessage("获取publicId为"+publicId+"的线路");
+
+        return ret;
+    }
+
+    @GetMapping
+    public HichinaResponse getListOfLinesPagedWithSearchKeyword(@RequestParam(value = "page", required = true) Integer page,
+                                                                @RequestParam(value = "pageSize", required = true) Integer size,
+                                                                @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        PageHelper.startPage(page,size);
+        //int defaultPageSize = env.getProperty("default.page.size", Integer.class)PageHelper.startPage(page,size);
+
+        List<HichinaLine> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            rawData = hichinaLineMapper.findLinesByQuery(query);
+            cnt = hichinaLineMapper.countByQuery(query);
+        }else{
+            rawData = hichinaLineMapper.findAllLines();
+            cnt = hichinaLineMapper.count();
+        }
+        ret.setMessage("Successfully got all lines");
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+}

+ 51 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ImageController.java

@@ -0,0 +1,51 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.aliyun.oss.OSSClient;
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+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 java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("/api/v1/image")
+public class ImageController {
+    private static final Logger LOG = LoggerFactory.getLogger(ImageController.class);
+
+    @Autowired
+    private Environment env;
+
+    @RequestMapping(value = "/upload", method = RequestMethod.POST)
+    public HichinaResponse uploadProfile(@RequestParam("imageFile") MultipartFile uploadFile, @RequestParam("expectedType") String expectedType) {
+        HichinaResponse ret = new HichinaResponse();
+        try {
+            byte[] profileBytes = uploadFile.getBytes();
+            String BLOG_BUCKET_NAME = env.getProperty("aliyun.blog.bucket");
+            String endpoint = env.getProperty("aliyun.oss.endpoint");
+            String ossDomain = env.getProperty("aliyun.oss.endpoint.domain");
+            String accessKeyId = env.getProperty("hichina.root.access.key.id");
+            String accessKeySecret = env.getProperty("hichina.root.access.key.secret");
+            OSSClient ossClient = new OSSClient(endpoint, accessKeyId,accessKeySecret);
+
+            String key = java.util.UUID.randomUUID().toString()+".jpg";
+            ossClient.putObject(BLOG_BUCKET_NAME, key, new ByteArrayInputStream(profileBytes));
+            ossClient.shutdown();
+            ret.setOk(true);
+            String style = expectedType.equals("blogImage")?"blogcontent":"thumbnail";
+            String url = "https://"+BLOG_BUCKET_NAME+"."+ossDomain+"/"+key+"?x-oss-process=style/"+style;
+            ret.setData(url);
+            return ret;
+        } catch (IOException e) {
+            LOG.error("=============="+e.getMessage()+"========");
+            return null;
+        }
+    }
+}

+ 69 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/LoginController.java

@@ -0,0 +1,69 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.hichina.admin.hichinaadminbackend.mapper.UserMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.LoginTestDTO;
+import com.hichina.admin.hichinaadminbackend.model.User;
+import org.springframework.beans.factory.annotation.Autowired;
+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 java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/testlogin")
+
+
+public class LoginController {
+    @Autowired
+    private UserMapper userMapper;
+
+    @PostMapping
+    public Boolean tryLogin(@RequestBody LoginTestDTO request){
+        List<User> users =  userMapper.findByEmail(request.getEmail());
+        if(users.isEmpty()){
+            return false;
+        }
+        User user = users.get(0);
+
+        Integer pwdCode = user.getPwdCode();
+        String passwordInDB = user.getPassword();
+        String plainText = request.getPassword();
+
+        return validatePassword(plainText, passwordInDB, pwdCode);
+    }
+
+    private String md5(String input){
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("MD5");
+            //calculating message digest of an input that return array of byte
+            byte[] messageDigest = md.digest(input.getBytes());
+
+            //converting byte array into signum representation
+            BigInteger no = new BigInteger(1, messageDigest);
+
+            //converting message digest into hex value
+            String hashtext = no.toString(16);
+            while (hashtext.length() < 32)
+            {
+                hashtext = "0" + hashtext;
+            }
+            return hashtext;
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+
+    private Boolean validatePassword(String plainText, String passwordInDB, Integer pwdCode){
+        String encrypted = md5(plainText+(pwdCode==null?"":pwdCode.toString()));
+
+        return passwordInDB.equals(encrypted);
+    }
+}

+ 75 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/OrderController.java

@@ -0,0 +1,75 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.OrderMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import com.hichina.admin.hichinaadminbackend.model.DTO.OrderDetailDTO;
+import com.hichina.admin.hichinaadminbackend.model.DTO.OrderListDTO;
+import com.hichina.admin.hichinaadminbackend.model.DTO.PaginationWrapper;
+import com.hichina.admin.hichinaadminbackend.model.Destination;
+import com.hichina.admin.hichinaadminbackend.model.Order;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/order")
+public class OrderController {
+    @Autowired
+    private OrderMapper orderMapper;
+
+    @GetMapping("/{orderId}")
+    public HichinaResponse getOrderById(@PathVariable("orderId") String orderId) throws UnsupportedEncodingException {
+        HichinaResponse ret = new HichinaResponse();
+
+        List<OrderDetailDTO> orderDetailDTOS = orderMapper.findOrderDetailById(orderId);
+
+        if(orderDetailDTOS.isEmpty()){
+            throw new RuntimeException("Cannot get any order by id: "+ orderId);
+        }
+
+        OrderDetailDTO orderDetail = orderDetailDTOS.get(0);
+        orderDetail.setMeta(Base64.getEncoder().encodeToString(orderDetail.getMeta().getBytes("UTF-8")));
+        ret.setData(orderDetail);
+        ret.setMessage("successfully get order detail by id:"+orderId);
+        ret.setOk(true);
+        return ret;
+    }
+
+    @GetMapping
+    public HichinaResponse getOrders(@RequestParam(value = "page", required = true) Integer page,
+                                           @RequestParam(value = "pageSize", required = true) Integer size,
+                                           @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+        List<OrderListDTO> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            rawData = orderMapper.findOrdersByQuery(query);
+            cnt = orderMapper.countByQuery(query);
+        }else{
+            rawData = orderMapper.findAllOrders();
+            cnt = orderMapper.count();
+        }
+
+        ret.setMessage("Successfully got all orders");
+
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+}

+ 94 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/PageContentAdminController.java

@@ -0,0 +1,94 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import com.hichina.admin.hichinaadminbackend.model.mongo.BlogSlideImage;
+import com.hichina.admin.hichinaadminbackend.model.mongo.GuidebookIntroSlideImage;
+import com.hichina.admin.hichinaadminbackend.model.mongo.HomeSlideImage;
+import com.hichina.admin.hichinaadminbackend.repository.BlogSlideImageRepository;
+import com.hichina.admin.hichinaadminbackend.repository.GuidebookIntroSlideImageRepository;
+import com.hichina.admin.hichinaadminbackend.repository.HomeSlideImageRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/pagecontent")
+public class PageContentAdminController {
+    @Autowired
+    private GuidebookIntroSlideImageRepository guidebookIntroSlideImageRepository;
+
+    @Autowired
+    private HomeSlideImageRepository homeSlideImageRepository;
+
+    @Autowired
+    private BlogSlideImageRepository blogSlideImageRepository;
+
+    @PostMapping("/guidebookintrosliders")
+    public HichinaResponse setGuidebookIntroSliders(@RequestBody List<GuidebookIntroSlideImage> sliders){
+        guidebookIntroSlideImageRepository.deleteAll();
+        guidebookIntroSlideImageRepository.insert(sliders);
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setMessage("成功设置攻略页轮播图");
+        ret.setOk(true);
+        ret.setData(sliders);
+
+        return ret;
+    }
+
+    @PostMapping("/homesliders")
+    public HichinaResponse setHomeSliders(@RequestBody List<HomeSlideImage> sliders){
+        homeSlideImageRepository.deleteAll();
+        homeSlideImageRepository.insert(sliders);
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setMessage("成功设置首页轮播图");
+        ret.setOk(true);
+        ret.setData(sliders);
+
+        return ret;
+    }
+
+    @PostMapping("/bloghomeliders")
+    public HichinaResponse setBlogSliders(@RequestBody List<BlogSlideImage> sliders){
+        blogSlideImageRepository.deleteAll();
+        blogSlideImageRepository.insert(sliders);
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setMessage("成功设置博客页面轮播图");
+        ret.setOk(true);
+        ret.setData(sliders);
+        return ret;
+    }
+
+    @GetMapping("/bloghomeliders")
+    public HichinaResponse getAllBlogSliders(){
+        HichinaResponse ret = new HichinaResponse();
+        List<BlogSlideImage> sliders = blogSlideImageRepository.findAll();
+        ret.setData(sliders);
+        ret.setOk(true);
+        ret.setMessage("成功获取blog首页所有轮播图");
+        return ret;
+    }
+
+    @GetMapping("/guidebookintrosliders")
+    public HichinaResponse getAllGuidebookIntroSliders(){
+        HichinaResponse ret = new HichinaResponse();
+        List<GuidebookIntroSlideImage> sliders = guidebookIntroSlideImageRepository.findAll();
+        ret.setData(sliders);
+        ret.setOk(true);
+        ret.setMessage("成功获取所有轮播图");
+        return ret;
+    }
+
+    @GetMapping("/homesliders")
+    public HichinaResponse getAllHomeSliders(){
+        HichinaResponse ret = new HichinaResponse();
+        List<HomeSlideImage> sliders = homeSlideImageRepository.findAll();
+        ret.setData(sliders);
+        ret.setOk(true);
+        ret.setMessage("成功获取所有轮播图");
+        return ret;
+    }
+}

+ 83 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductSharedAttributesController.java

@@ -0,0 +1,83 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.ProductAttributeMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/productsharedattributes")
+public class ProductSharedAttributesController {
+    @Autowired
+    private ProductAttributeMapper productAttributeMapper;
+
+    @GetMapping
+    public HichinaResponse getPagedSharedAttributes(@RequestParam(value = "page", required = true) Integer page,
+                                                    @RequestParam(value = "pageSize", required = true) Integer size,
+                                                    @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+        List<ProductAttribute> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            rawData = productAttributeMapper.findAttributesByQuery(query);
+            cnt = productAttributeMapper.countByQuery(query);
+        }else{
+            rawData = productAttributeMapper.findAllAttributes();
+            cnt = productAttributeMapper.count();
+        }
+        ret.setMessage("成功获取所有属性");
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+
+    @PostMapping
+    public HichinaResponse createSharedAttributes(@RequestBody ProductAttributeCreateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        ProductAttribute productAttribute = new ProductAttribute();
+        productAttribute.setAttributeName(request.getName());
+        productAttribute.setAttributeId(java.util.UUID.randomUUID().toString());
+        productAttribute.setDataType(request.getDataType());
+
+        try{
+            productAttributeMapper.insert(productAttribute);
+            ret.setData(productAttribute.getAttributeId());
+            ret.setOk(true);
+            ret.setMessage("成功创建属性");
+        }catch (Exception e){
+            ret.setData(null);
+            ret.setOk(false);
+            ret.setMessage("创建属性失败"+e.getMessage());
+        }
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    public HichinaResponse deleteProductAttributes(@RequestBody ProductAttributeBatchDeleteRequest req){
+
+        // TODO: check if the attributes has any reference in the attribute value table, if yes, block
+        productAttributeMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除属性");
+
+        return ret;
+    }
+}

+ 363 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductSkuController.java

@@ -0,0 +1,363 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.*;
+import com.hichina.admin.hichinaadminbackend.model.*;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import com.hichina.admin.hichinaadminbackend.util.UserUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.util.Strings;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/api/v1/productsku")
+public class ProductSkuController {
+    @Autowired
+    private HichinaProductMapper hichinaProductMapper;
+
+    @Autowired
+    private UserUtil userUtil;
+
+    @Autowired ProductSkuGroupMapper productSkuGroupMapper;
+
+    @Autowired
+    private ProductSkuTimestampAttributeMappingMapper productSkuTimestampAttributeMappingMapper;
+
+    @Autowired
+    private ProductSkuVarcharAttributeMappingMapper productSkuVarcharAttributeMappingMapper;
+
+    @Autowired
+    private ProductSkuIntAttributeMappingMapper productSkuIntAttributeMappingMapper;
+
+    private ProductPropertyBag changeDateStringFormat(ProductPropertyBag input){
+        String dateString = input.getAttributeValue();
+
+        //2023-03-22 00:00:00
+        SimpleDateFormat inputformatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
+        Date date = null;
+        try {
+            date = inputformatter.parse(dateString);
+            SimpleDateFormat outputformatter = new SimpleDateFormat("yyyy/MM/dd");
+            String formatted = outputformatter.format(date);
+            input.setAttributeValue(formatted);
+        } catch (ParseException e) {
+            throw new RuntimeException("Invalid date format");
+        }
+        return input;
+    }
+    /**
+     * main logic to get custom property bag, each skuId can have more then 1 property from each typed attribute table
+     * @param skuId
+     * @return
+     */
+    private List<ProductPropertyBag> getPropertyBagBySkuId(String skuId){
+        List<ProductPropertyBag> propertyBags = new ArrayList<>();
+
+        // TODO: 2023/3/18 if added other type, we should update here
+        List<ProductPropertyBag> intPropertyBag = productSkuIntAttributeMappingMapper.findAttributeValueBySkuId(skuId);
+        List<ProductPropertyBag> varcharPropertyBag = productSkuVarcharAttributeMappingMapper.findAttributeValueBySkuId(skuId);
+        List<ProductPropertyBag> datePropertyBag = productSkuTimestampAttributeMappingMapper.findAttributeValueBySkuId(skuId);
+        datePropertyBag = datePropertyBag.stream().map(r->changeDateStringFormat(r)).collect(Collectors.toList());
+
+        propertyBags.addAll(intPropertyBag);
+        propertyBags.addAll(varcharPropertyBag);
+        propertyBags.addAll(datePropertyBag);
+
+        propertyBags.sort(Comparator.comparing(ProductPropertyBag::getSequence));
+
+        List<ProductAttributeTypeShortDTO> bindedAttributes = hichinaProductMapper.findBindedAttributesBySkuId(skuId);
+
+        List<ProductPropertyBag> finalPropertyBag = fillEmptyPropertyBag(propertyBags, bindedAttributes);
+
+        return finalPropertyBag;
+    }
+
+    /**
+    check if all attribute Id in bindedAttributes is covered by propertyBags, otherwise add empty ProductPropertyBag to make up for the lost
+     */
+    private List<ProductPropertyBag> fillEmptyPropertyBag(List<ProductPropertyBag> propertyBags, List<ProductAttributeTypeShortDTO> bindedAttributes){
+        List<String> existingAttributeIds = propertyBags.stream().map(r ->r.getAttributeId()).collect(Collectors.toList());
+
+        List<ProductPropertyBag> toAdd = new ArrayList<>();
+
+        for(ProductAttributeTypeShortDTO att : bindedAttributes){
+            if(existingAttributeIds.isEmpty() || (!existingAttributeIds.isEmpty() && !existingAttributeIds.contains(att.getAttributeId()))){
+                ProductPropertyBag placeHolderObj = new ProductPropertyBag();
+                placeHolderObj.setAttributeId(att.getAttributeId());
+                placeHolderObj.setDataType(att.getDataType());
+                placeHolderObj.setAttributeValue(null);
+                placeHolderObj.setAttributeName(att.getAttributeName());
+                toAdd.add(placeHolderObj);
+            }
+        }
+        propertyBags.addAll(toAdd);
+
+        return propertyBags;
+    }
+
+    @GetMapping("/withpropertybag")
+    public HichinaResponse getProductSkuById(@RequestParam(value="skuId",required = true) String skuId){
+        HichinaResponse ret = new HichinaResponse();
+
+        // get the basic property
+        List<HichinaProductDTO> singleSkuInList = hichinaProductMapper.findBySkuId(skuId);
+
+
+        HichinaProductDTO singleSku;
+        if(!singleSkuInList.isEmpty()){
+            singleSku = singleSkuInList.get(0);
+        }else{
+            singleSku = null;
+        }
+
+        List<ProductPropertyBag> propertyBags = getPropertyBagBySkuId(skuId);
+
+        HichinaProductWithPropertyBagDTO data = new HichinaProductWithPropertyBagDTO();
+        data.setHichinaProductDTO(singleSku);
+        data.setProductPropertyBag(propertyBags);
+
+        ret.setMessage("成功获取skuId: "+skuId+"的所有属性");
+        ret.setData(data);
+        ret.setOk(true);
+
+        return ret;
+    }
+    private boolean checkUseMineOnly(String currentUserName){
+        AdminUser adminUser = userUtil.currentUser(currentUserName);
+        return adminUser.getUsername().startsWith("SP_");
+    }
+
+    @GetMapping
+    public HichinaResponse getProductSkuNoCustomAttributes(@RequestParam(value = "page", required = true) Integer page,
+                                                                @RequestParam(value = "pageSize", required = true) Integer size,
+                                                                @RequestParam(value = "query") String query,
+                                         @RequestParam(value = "productTypeId") String productTypeId){
+        HichinaResponse ret = new HichinaResponse();
+        String currentUser = userUtil.currentUserName();
+        boolean mineOnly = checkUseMineOnly(currentUser);
+
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+
+        List<HichinaProductDTO> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            if(!StringUtils.isEmpty(productTypeId)){
+                rawData = hichinaProductMapper.findByProductTypeIdAndQuery(productTypeId, query, mineOnly, currentUser);
+                cnt = hichinaProductMapper.countByProductTypeIdAndQuery(productTypeId, query, mineOnly, currentUser);
+            }else{
+                rawData = hichinaProductMapper.findByQuery(query, mineOnly, currentUser);
+                cnt = hichinaProductMapper.countByQuery(query, mineOnly, currentUser);
+            }
+        }else{
+            if(!StringUtils.isEmpty(productTypeId)){
+                rawData = hichinaProductMapper.findByProductTypeId(productTypeId, mineOnly, currentUser);
+                cnt = hichinaProductMapper.countByProductTypeId(productTypeId, mineOnly, currentUser);
+            }else{
+                rawData = hichinaProductMapper.findAllProduct(mineOnly, currentUser);
+                cnt = hichinaProductMapper.count(mineOnly, currentUser);
+            }
+        }
+        ret.setMessage("Successfully got all lines");
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+
+    /**
+     * whether exist or not, return the group id, if not exist, insert first
+     * @param productName
+     * @param productTypeId
+     * @return
+     */
+    private String checkProductGroupExistence(String productName, String productTypeId){
+        List<ProductSkuGroup> productSkuGroups = productSkuGroupMapper.findByNameAndProductTypeId(productName, productTypeId);
+        String productSkuGroupId;
+        if(productSkuGroups.isEmpty()){
+            productSkuGroupId = java.util.UUID.randomUUID().toString();
+            productSkuGroupMapper.insert(new ProductSkuGroup(productSkuGroupId, productName, productTypeId , new Date(), true));
+        }else{
+            productSkuGroupId = productSkuGroups.get(0).getSkuGroupId();
+        }
+        return productSkuGroupId;
+    }
+
+    private String insertProductSkuInTransaction(ProductSkuCreateDTO request){
+        HichinaProduct product = new HichinaProduct();
+        product.setProductName(request.getProductName());
+        product.setProductContent(request.getProductDescription());
+        product.setProductTypeId(request.getProductTypeId());
+        product.setSkuId(java.util.UUID.randomUUID().toString());
+        product.setCreatedTime(new Date());
+        product.setCreatedBy(userUtil.currentUserName());
+        // check if sku group of name request.getProductName() and request.getProductTypeId() exist, if not, insert new sku group, and set corresponding sku_group_id in product
+        String productSkuGroupId = checkProductGroupExistence(request.getProductName(), request.getProductTypeId());
+        product.setSkuGroupId(productSkuGroupId);
+        // insert common attribute
+        hichinaProductMapper.insert(product);
+        // note to make all in a single transaction
+        // insert custom property bag into various tables
+        for (Map.Entry<String, String> entry : request.getCustomPropertyBag().entrySet()) {
+            String dataTypeAndAttributeId = entry.getKey();
+            String value = entry.getValue();
+            String dataType = dataTypeAndAttributeId.substring(dataTypeAndAttributeId.indexOf("[")+1, dataTypeAndAttributeId.indexOf("]"));
+            String attributeId = dataTypeAndAttributeId.substring(dataTypeAndAttributeId.indexOf("]")+1);
+            if(dataType.equals("string") || dataType.equals("image")  || dataType.equals("datestring")){
+                ProductSkuVarcharAttributeMapping productSkuVarcharAttributeMapping = new ProductSkuVarcharAttributeMapping();
+                productSkuVarcharAttributeMapping.setSkuId(product.getSkuId());
+                productSkuVarcharAttributeMapping.setAttributeValue(value);
+                productSkuVarcharAttributeMapping.setDataType(dataType);
+                productSkuVarcharAttributeMapping.setAttributeId(attributeId);
+                productSkuVarcharAttributeMappingMapper.insert(productSkuVarcharAttributeMapping);
+            }else if(dataType.equals("integer")){
+                ProductSkuIntAttributeMapping productSkuIntAttributeMapping = new ProductSkuIntAttributeMapping();
+                productSkuIntAttributeMapping.setSkuId(product.getSkuId());
+                productSkuIntAttributeMapping.setAttributeValue(Integer.parseInt(value));
+                productSkuIntAttributeMapping.setDataType(dataType);
+                productSkuIntAttributeMapping.setAttributeId(attributeId);
+                productSkuIntAttributeMappingMapper.insert(productSkuIntAttributeMapping);
+            }else if(dataType.equals("date")){
+                ProductSkuTimestampAttributeMapping productSkuTimestampAttributeMapping = new ProductSkuTimestampAttributeMapping();
+                productSkuTimestampAttributeMapping.setSkuId(product.getSkuId());
+                SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd", Locale.ENGLISH);
+                Date date = null;
+                try {
+                    date = formatter.parse(value);
+                    productSkuTimestampAttributeMapping.setAttributeValue(date);
+                    productSkuTimestampAttributeMapping.setDataType(dataType);
+                    productSkuTimestampAttributeMapping.setAttributeId(attributeId);
+                    productSkuTimestampAttributeMappingMapper.insert(productSkuTimestampAttributeMapping);
+                } catch (ParseException e) {
+                    throw new RuntimeException("Invalid date format");
+                }
+            }else{
+                throw new RuntimeException("Not supported data type!");
+            }
+        }
+        return product.getSkuId();
+    }
+    @PostMapping
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)
+    public HichinaResponse createProductSku(@RequestBody ProductSkuCreateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        String skuId = insertProductSkuInTransaction(request);
+
+        ret.setData(skuId);
+        ret.setOk(true);
+        ret.setMessage("成功新建产品");
+
+        return ret;
+    }
+
+    @PutMapping("/withpropertybags/{skuId}")
+    @Transactional
+    public HichinaResponse updateProductWithAllProperties(@PathVariable("skuId") String skuId ,@RequestBody ProductSkuUpdateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        List<HichinaProduct> hichinaProducts = hichinaProductMapper.findRawBySkuId(skuId);
+        if(hichinaProducts.isEmpty()){
+            throw new RuntimeException("Nothing to update");
+        }
+        HichinaProduct toUpdate = hichinaProducts.get(0);
+        if(!request.getProductName().equals(toUpdate.getProductName())){
+            // indicating name change, so must update sku group id
+            String skuGroupId = checkProductGroupExistence(request.getProductName(), toUpdate.getProductTypeId());
+            toUpdate.setSkuGroupId(skuGroupId);
+        }
+        toUpdate.setProductName(request.getProductName());
+        toUpdate.setProductContent(request.getProductContent());
+        // update basic info
+        hichinaProductMapper.updateBySkuId(toUpdate);
+
+        productSkuGroupMapper.deleteUnreferenced();
+
+        // update property bags
+        List<ProductPropertyBag> propertyBags = request.getPropertyBags();
+
+        updatePropertyBagsBySkuId(skuId, propertyBags);
+
+        ret.setOk(true);
+        ret.setData(skuId);
+        ret.setMessage("成功更新skuId:"+skuId+"的产品");
+
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    @Transactional
+    public HichinaResponse deleteProductSkus(@RequestBody ProductSkuBatchDeleteRequest req){
+
+        // delete all related custom properties
+        productSkuTimestampAttributeMappingMapper.batchDelete(req.getToDelete());
+        productSkuIntAttributeMappingMapper.batchDelete(req.getToDelete());
+        productSkuVarcharAttributeMappingMapper.batchDelete(req.getToDelete());
+        // delete base product record
+        hichinaProductMapper.batchDelete(req.getToDelete());
+        // remove unreferenced group
+        productSkuGroupMapper.deleteUnreferenced();
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除product sku");
+
+        return ret;
+    }
+
+    private void updatePropertyBagsBySkuId(String skuId, List<ProductPropertyBag> propertyBags){
+        // first filter out those propertybag with no value
+        List<ProductPropertyBag> nonEmptyPropertyBag  = propertyBags.stream().filter(r -> !Strings.isEmpty(r.getAttributeValue())).collect(Collectors.toList());
+
+        for(ProductPropertyBag item: nonEmptyPropertyBag){
+            if("integer".equals(item.getDataType())){
+                ProductSkuIntAttributeMapping upsertObj = new ProductSkuIntAttributeMapping();
+                upsertObj.setAttributeId(item.getAttributeId());
+                upsertObj.setSkuId(skuId);
+                upsertObj.setDataType(item.getDataType());
+                upsertObj.setAttributeValue(Integer.parseInt(item.getAttributeValue()));
+                productSkuIntAttributeMappingMapper.upsertBySkuIdAndAttributeId(upsertObj);
+            }else if("string".equals(item.getDataType()) || "image".equals(item.getDataType()) || "datestring".equals(item.getDataType())){
+                ProductSkuVarcharAttributeMapping upsertObj = new ProductSkuVarcharAttributeMapping();
+                upsertObj.setAttributeId(item.getAttributeId());
+                upsertObj.setSkuId(skuId);
+                upsertObj.setDataType(item.getDataType());
+                upsertObj.setAttributeValue(item.getAttributeValue());
+                productSkuVarcharAttributeMappingMapper.upsertBySkuIdAndAttributeId(upsertObj);
+            }else if("date".equals(item.getDataType())){
+                ProductSkuTimestampAttributeMapping upsertObj = new ProductSkuTimestampAttributeMapping();
+                upsertObj.setAttributeId(item.getAttributeId());
+                upsertObj.setSkuId(skuId);
+                upsertObj.setDataType(item.getDataType());
+
+                SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd", Locale.ENGLISH);
+                Date date = null;
+                try {
+                    date = formatter.parse(item.getAttributeValue());
+                    upsertObj.setAttributeValue(date);
+                    productSkuTimestampAttributeMappingMapper.upsertBySkuIdAndAttributeId(upsertObj);
+                }catch (ParseException e) {
+                    throw new RuntimeException("Invalid date format");
+                }
+            }else{
+                // do nothing
+            }
+        }
+    }
+}

+ 138 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/ProductTypeController.java

@@ -0,0 +1,138 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.ProductTypeMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import com.hichina.admin.hichinaadminbackend.model.ProductType;
+import com.hichina.admin.hichinaadminbackend.model.ProductTypeAttributeMapping;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/producttype")
+public class ProductTypeController {
+
+    @Autowired
+    private ProductTypeMapper productTypeMapper;
+
+    @GetMapping("/bindedattributesforproduct")
+    public HichinaResponse getProductTypeBingingAttr(@RequestParam(value = "productTypeId", required = true) String productTypeId){
+        HichinaResponse ret = new HichinaResponse();
+
+        List<ProductAttribute>  attributes = productTypeMapper.findAttributesByProductType(productTypeId);
+
+        ret.setMessage("成功获取产品类型:"+productTypeId+"所绑定的属性");
+        ret.setOk(true);
+        ret.setData(attributes);
+
+        return ret;
+    }
+
+    @GetMapping
+    public HichinaResponse getPagedProductType(@RequestParam(value = "page", required = true) Integer page,
+                                                    @RequestParam(value = "pageSize", required = true) Integer size,
+                                                    @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        if(size>0){
+            PageHelper.startPage(page,size);
+        }
+        List<ProductType> rawData;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            rawData = productTypeMapper.findProductTypeByQuery(query);
+            cnt = productTypeMapper.countByQuery(query);
+        }else{
+            rawData = productTypeMapper.findAllProductType();
+            cnt = productTypeMapper.count();
+        }
+        ret.setMessage("成功获取所有产品类型");
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(rawData);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+
+        return ret;
+    }
+
+    @PostMapping
+    public HichinaResponse createProductType(@RequestBody ProductTypeCreateDTO request){
+        HichinaResponse ret = new HichinaResponse();
+
+        ProductType productType = new ProductType();
+        productType.setProductTypeName(request.getName());
+        productType.setProductTypeDescription(request.getDescription());
+        productType.setProductTypeId(java.util.UUID.randomUUID().toString());
+
+        try{
+            productTypeMapper.insert(productType);
+            ret.setData(productType.getProductTypeId());
+            ret.setOk(true);
+            ret.setMessage("成功创建产品类型");
+        }catch (Exception e){
+            ret.setData(null);
+            ret.setOk(false);
+            ret.setMessage("创建产品类型失败"+e.getMessage());
+        }
+        return ret;
+    }
+
+    @DeleteMapping("/batch")
+    public HichinaResponse deleteProductTypes(@RequestBody ProductTypeBatchDeleteRequestDTO req){
+
+        // TODO: 2023/3/11 don't allow delete if the product type has any reference by any product sku 
+        productTypeMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除产品类别");
+
+        return ret;
+    }
+
+    @PostMapping("/unbindattr")
+    public HichinaResponse unbindAttributeFromProductType(@RequestBody BindAtrribute2ProductRequest req){
+        // TODO: 2023/3/12 add constraint that if any attribute of this type is in use, don't allow unbind
+        HichinaResponse ret = new HichinaResponse();
+        try{
+            productTypeMapper.unbindAttr(req.getProductTypeId(), req.getAttributeId());
+            ret.setOk(true);
+            ret.setMessage("成功解绑产品类型到属性的关联");
+        }catch (Exception e){
+            ret.setOk(false);
+            ret.setMessage("解绑产品类型到属性的关联失败"+e.getMessage());
+        }
+        return ret;
+    }
+
+    @PostMapping("/bindattr")
+    public HichinaResponse bindAtrributeWithProductType(@RequestBody BindAtrribute2ProductRequest req){
+        HichinaResponse ret = new HichinaResponse();
+        ProductTypeAttributeMapping productTypeAttributeMapping = new ProductTypeAttributeMapping();
+
+        productTypeAttributeMapping.setAttributeId(req.getAttributeId());
+        productTypeAttributeMapping.setProductTypeId(req.getProductTypeId());
+        productTypeAttributeMapping.setSequence(String.valueOf(System.currentTimeMillis()));
+
+
+        try{
+            productTypeMapper.bindAttr(productTypeAttributeMapping);
+
+            ret.setData(productTypeAttributeMapping.toString());
+            ret.setOk(true);
+            ret.setMessage("成功创建产品类型到属性的关联");
+        }catch (Exception e){
+            ret.setData(null);
+            ret.setOk(false);
+            ret.setMessage("创建产品类型到属性的关联失败"+e.getMessage());
+        }
+        return ret;
+    }
+}

+ 19 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/PublicController.java

@@ -0,0 +1,19 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/public")
+public class PublicController {
+    @Autowired
+    private Environment env;
+
+    @GetMapping("/servingurl")
+    public String getPublicServiceUrl(){
+        return env.getProperty("base.serving.url");
+    }
+}

+ 105 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/SkuGroupController.java

@@ -0,0 +1,105 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.ProductSkuGroupMapper;
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaResponse;
+import com.hichina.admin.hichinaadminbackend.model.DTO.PaginationWrapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.SkuGroupStatsDTO;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuGroup;
+import com.hichina.admin.hichinaadminbackend.util.UserUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/skugroup")
+public class SkuGroupController {
+    @Autowired
+    private UserUtil userUtil;
+
+    @Autowired
+    private ProductSkuGroupMapper productSkuGroupMapper;
+
+    private boolean checkUseMineOnly(String currentUserName){
+        AdminUser adminUser = userUtil.currentUser(currentUserName);
+        return adminUser.getUsername().startsWith("SP_");
+    }
+
+    @GetMapping("/groupstats")
+    public HichinaResponse getProductGroupStatsInfo(@RequestParam(value = "page", required = true) Integer page,
+                                                    @RequestParam(value = "pageSize", required = true) Integer size,
+                                                    @RequestParam(value = "query") String query){
+        HichinaResponse ret = new HichinaResponse();
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+        String currentUser = userUtil.currentUserName();
+        boolean mineOnly = checkUseMineOnly(currentUser);
+
+        List<SkuGroupStatsDTO> data;
+        int cnt = 0;
+        if(!StringUtils.isEmpty(query)){
+            data = productSkuGroupMapper.findSkuGroupStatsByQuery(query, mineOnly, currentUser);
+            cnt = productSkuGroupMapper.countSkuGroupStatsByQuery(query, mineOnly, currentUser);
+        }else{
+            data = productSkuGroupMapper.findAllSkuGroupStats(mineOnly, currentUser);
+            cnt = productSkuGroupMapper.countAllSkuGroupStats(mineOnly, currentUser);
+        }
+
+        ret.setOk(true);
+        ret.setMessage("成功获取sku group的统计数据");
+
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(data);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+
+        ret.setData(paginationWrapper);
+        return ret;
+    }
+
+    private void enableDisableGroup(String skuGroupId, Boolean toEnable){
+        List<ProductSkuGroup> groups = productSkuGroupMapper.findBySkuGroupId(skuGroupId);
+
+        if(groups.isEmpty()){
+            throw new RuntimeException("No group to update");
+        }
+
+        ProductSkuGroup toUpdate = groups.get(0);
+
+        toUpdate.setEnabled(toEnable);
+
+        productSkuGroupMapper.update(toUpdate);
+    }
+
+    @PutMapping("/enable/{skuGroupId}")
+    public HichinaResponse enableGroup(@PathVariable("skuGroupId") String skuGroupId){
+        HichinaResponse ret = new HichinaResponse();
+
+        enableDisableGroup(skuGroupId, true);
+
+        ret.setData(skuGroupId);
+        ret.setOk(true);
+        ret.setMessage("成功启用sku group: "+ skuGroupId);
+
+        return ret;
+    }
+
+    @PutMapping("/disable/{skuGroupId}")
+    public HichinaResponse disableGroup(@PathVariable("skuGroupId") String skuGroupId){
+        HichinaResponse ret = new HichinaResponse();
+
+        enableDisableGroup(skuGroupId, false);
+
+        ret.setData(skuGroupId);
+        ret.setOk(true);
+        ret.setMessage("成功停用sku group: "+ skuGroupId);
+
+        return ret;
+    }
+}

+ 24 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/UserController.java

@@ -0,0 +1,24 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.hichina.admin.hichinaadminbackend.util.UserUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/v1/user/info")
+public class UserController {
+
+    @Autowired
+    private UserUtil userUtil;
+
+    @GetMapping()
+    public String whoami(){
+        return currentUserName();
+    }
+
+    public String currentUserName(){
+        return userUtil.currentUserName();
+    }
+}

+ 32 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/AdminUserMapper.java

@@ -0,0 +1,32 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "adminUserMapper")
+public interface AdminUserMapper {
+
+    @Select("select * from admin_user where username=#{username}")
+    List<AdminUser> getUserByName(String username);
+
+    @Delete("<script>" +
+            "Delete FROM admin_user WHERE username in \n" +
+            "    <foreach item='item' collection='usernames' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> usernames);
+
+    @Select("select * from admin_user order by created_time desc")
+    List<AdminUser> getAllAdminUser();
+
+    @Insert("INSERT INTO admin_user(username, password, created_time) VALUES(#{username},#{password}, #{createdTime})")
+    void insert(AdminUser adminUser);
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/BlogMapper.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Update;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+@Mapper
+@Component(value = "blogMapper")
+public interface BlogMapper {
+    @Update("UPDATE blog SET head_image_url = REGEXP_SUBSTR(content, '(http|https)://[^ \\n]+(jpg|jpeg|png|gif)') WHERE last_update_time>#{startDate} and head_image_url is null or LENGTH(head_image_url)<1 and content REGEXP '(http|https)://[^ \\n]+(jpg|jpeg|png|gif)';")
+    void updateCoverWithFirstImage(Date startDate);
+}

+ 52 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/DestinationMapper.java

@@ -0,0 +1,52 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.Destination;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "destinationMapper")
+public interface DestinationMapper {
+    @Insert("INSERT INTO destination(destination_id, destination_name, level, parent_id, description, destination_profile_image, created_date) VALUES(#{destinationId},#{destinationName}, #{level}, #{parentId}, #{description}, #{destinationProfileImage}, #{createdDate})")
+    void insert(Destination destination);
+
+    @Delete("<script>" +
+            "Delete FROM destination WHERE destination_id in \n" +
+            "    <foreach item='item' collection='destinationIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> destinationIds);
+
+    @Update("update destination SET destination_name=#{destinationName}, level=#{level}, parent_id=#{parentId}, description=#{description}, destination_profile_image=#{destinationProfileImage}, created_date=#{createdDate} where destination_id=#{destinationId}")
+    void update(Destination destination);
+
+    @Select("select * from destination order by created_date desc")
+    List<Destination> findAllDestinations();
+
+    @Select("select * from destination where level=#{level} order by created_date desc")
+    List<Destination> findDestinationsByLevel(Integer level);
+
+    @Select("select * from destination where destination_id=#{id}")
+    List<Destination> findDestinationById(String id);
+
+    @Select("select count(*) from destination")
+    Integer count();
+
+    @Select("select count(*) from destination where level=#{level}")
+    Integer countByLevel(Integer level);
+
+    @Select("select * from destination where destination_name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%')) order by created_date desc")
+    List<Destination> findDestinationsByQuery(String query);
+
+    @Select("select * from destination where destination_name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%')) and level=#{level} order by created_date desc")
+    List<Destination> findDestinationsByQueryAndLevel(String query, Integer level);
+
+    @Select("select count(*) from destination where destination_name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countByQuery(String query);
+
+    @Select("select count(*) from destination where destination_name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%')) and leve=#{level}")
+    Integer countByQueryAndLevel(String query, Integer level);
+}

+ 45 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/GuidebookMapper.java

@@ -0,0 +1,45 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.GuidebookListDTO;
+import com.hichina.admin.hichinaadminbackend.model.Destination;
+import com.hichina.admin.hichinaadminbackend.model.Guidebook;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "guidebookMapper")
+public interface GuidebookMapper {
+    @Insert("INSERT INTO guidebook(guide_id, download_url, cover_image_url, short_description, destination_id, created_date) VALUES(#{guideId},#{downloadUrl}, #{coverImageUrl}, #{shortDescription}, #{destinationId}, #{createdDate})")
+    Long insert(Guidebook guidebook);
+
+    @Delete("<script>" +
+            "Delete FROM guidebook WHERE guide_id in \n" +
+            "    <foreach item='item' collection='guideIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> guideIds);
+
+    @Select("select g.guide_id , g.download_url , g.cover_image_url , g.short_description , g.destination_id , d.destination_name  from guidebook g inner join destination d on d.destination_id = g.destination_id where d.destination_name like CONCAT('%',CONCAT(#{query},'%')) or g.short_description like CONCAT('%',CONCAT(#{query},'%')) order by g.created_date desc")
+    List<GuidebookListDTO> findGuidebookByQuery(String query);
+
+    @Select("select * from guidebook where guide_id=#{guideId}")
+    List<Guidebook> findRawByGuideId(String guideId);
+
+    @Update("update guidebook SET guide_id=#{guideId}, cover_image_url=#{coverImageUrl}, download_url=#{downloadUrl}, short_description=#{shortDescription}, destination_id=#{destinationId}, created_date=#{createdDate} where guide_id=#{guideId}")
+    void update(Guidebook guidebook);
+
+    @Select("select g.guide_id , g.download_url , g.cover_image_url , g.short_description , g.destination_id , d.destination_name  from guidebook g inner join destination d on d.destination_id = g.destination_id where g.guide_id=#{guideId}")
+    List<GuidebookListDTO> findGuidebookByGuideId(String guideId);
+
+    @Select("select g.guide_id , g.download_url , g.cover_image_url , g.short_description , g.destination_id , d.destination_name  from guidebook g inner join destination d on d.destination_id = g.destination_id order by g.created_date desc")
+    List<GuidebookListDTO> findAllGuidebook();
+
+    @Select("select count(*) from guidebook")
+    Integer count();
+
+    @Select("select count(*) from guidebook g inner join destination d on d.destination_id = g.destination_id where d.destination_name like CONCAT('%',CONCAT(#{query},'%')) or g.short_description like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countByQuery(String query);
+}

+ 46 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/HichinaLineMapper.java

@@ -0,0 +1,46 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.HichinaLine;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "hichinaLineMapper")
+public interface HichinaLineMapper {
+
+    @Insert("INSERT INTO hichina_line(name, public_id, created_date, icon_path, range_in_days, base_price, description) VALUES(#{name},#{publicId}, #{createdDate}, #{iconPath}, #{rangeInDays}, #{basePrice}, #{description})")
+    @Options(useGeneratedKeys=true, keyProperty="id")
+    Long insert(HichinaLine line);
+
+    @Delete("DELETE FROM hichina_line WHERE id =#{id}")
+    void delete(Long id);
+
+    @Update("update hichina_line SET description=#{description}, name=#{name}, icon_path=#{iconPath}, range_in_days=#{rangeInDays}, base_price=#{basePrice} where public_id=#{publicId}")
+    void update(HichinaLine line);
+
+
+    @Select("<script>" +
+            "Delete FROM hichina_line WHERE public_id in \n" +
+            "    <foreach item='item' collection='publicIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> publicIds);
+
+    @Select("select * from hichina_line where name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%')) order by created_date desc")
+    List<HichinaLine> findLinesByQuery(String query);
+
+    @Select("select * from hichina_line where public_id=#{publicId}")
+    List<HichinaLine> findLineByPublicId(String publicId);
+
+    @Select("select count(*) from hichina_line")
+    Integer count();
+
+    @Select("select count(*) from hichina_line where name like CONCAT('%',CONCAT(#{query},'%')) or description like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countByQuery(String query);
+
+    @Select("select * from hichina_line order by created_date desc")
+    List<HichinaLine> findAllLines();
+}

+ 68 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/HichinaProductMapper.java

@@ -0,0 +1,68 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.HichinaProductDTO;
+import com.hichina.admin.hichinaadminbackend.model.DTO.ProductAttributeTypeShortDTO;
+import com.hichina.admin.hichinaadminbackend.model.HichinaLine;
+import com.hichina.admin.hichinaadminbackend.model.HichinaProduct;
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "hichinaProductMapper")
+public interface HichinaProductMapper {
+
+    @Insert("INSERT INTO hichina_product(sku_id, sku_group_id, product_type_id, product_name, product_content, created_time, created_by) VALUES(#{skuId}, #{skuGroupId}, #{productTypeId},#{productName},#{productContent}, #{createdTime}, #{createdBy})")
+    @Options(keyProperty="sku_id")
+    void insert(HichinaProduct product);
+
+    @Delete("<script>" +
+            "Delete FROM hichina_product WHERE sku_id in \n" +
+            "    <foreach item='item' collection='toBeDel' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> toBeDel);
+
+    @Select("select pm.attribute_id, a.attribute_name, a.data_type from hichina_product p inner join product_type_attribute_mapping pm on p.product_type_id=pm.product_type_id inner join product_attribute a on a.attribute_id=pm.attribute_id where p.sku_id=#{skuId}")
+    List<ProductAttributeTypeShortDTO> findBindedAttributesBySkuId(String skuId);
+
+    @Select("<script> select sku_id, product_type_name, t.product_type_id, product_name, product_content, created_time from hichina_product p inner join hichina_product_type t on p.product_type_id=t.product_type_id  where <if test='mineOnly'> p.created_by=#{currentUser} and </if> p.product_type_id=#{productTypeId} and product_name like CONCAT('%',CONCAT(#{query},'%')) or product_content like CONCAT('%',CONCAT(#{query},'%')) order by p.created_time desc </script>")
+    List<HichinaProductDTO> findByProductTypeIdAndQuery(String productTypeId, String query, boolean mineOnly, String currentUser);
+
+    @Select("<script> select sku_id, product_type_name, t.product_type_id, product_name, product_content, created_time from hichina_product p inner join hichina_product_type t on p.product_type_id=t.product_type_id  <if test='mineOnly'> where p.created_by=#{currentUser} </if> order by p.created_time desc </script>")
+    List<HichinaProductDTO> findAllProduct(boolean mineOnly, String currentUser);
+
+
+    @Select("<script> select sku_id, product_type_name, t.product_type_id, product_name, product_content, created_time from hichina_product p inner join hichina_product_type t on p.product_type_id=t.product_type_id  where <if test='mineOnly'> p.created_by=#{currentUser} and </if> product_name like CONCAT('%',CONCAT(#{query},'%')) or product_content like CONCAT('%',CONCAT(#{query},'%')) order by p.created_time desc </script>")
+    List<HichinaProductDTO> findByQuery(String query, boolean mineOnly, String currentUser);
+
+    @Select("<script> select sku_id, product_type_name, product_name, product_content, created_time from hichina_product p inner join hichina_product_type t on p.product_type_id=t.product_type_id  where <if test='mineOnly'> p.created_by=#{currentUser} and </if> p.product_type_id=#{productTypeId} order by p.created_time desc </script>")
+    List<HichinaProductDTO> findByProductTypeId(String productTypeId, boolean mineOnly, String currentUser);
+
+    @Select("select sku_id, product_type_name, t.product_type_id, product_name, product_content, created_time from hichina_product p inner join hichina_product_type t on p.product_type_id=t.product_type_id  where p.sku_id=#{skuId} ")
+    List<HichinaProductDTO> findBySkuId(String skuId);
+
+    @Select("select * from hichina_product where sku_id=#{skuId}")
+    List<HichinaProduct> findRawBySkuId(String skuId);
+
+
+    @Select("<script> select count(*) from hichina_product where <if test='mineOnly'> created_by=#{currentUser} and </if> product_type_id=#{productTypeId} and product_name like CONCAT('%',CONCAT(#{query},'%')) or product_content like CONCAT('%',CONCAT(#{query},'%')) </script>")
+    int countByProductTypeIdAndQuery(String productTypeId, String query, boolean mineOnly, String currentUser);
+
+    @Select("<script> select count(*) from hichina_product where <if test='mineOnly'> p.created_by=#{currentUser} and </if> product_name like CONCAT('%',CONCAT(#{query},'%')) or product_content like CONCAT('%',CONCAT(#{query},'%')) </script>")
+    int countByQuery(String query, boolean mineOnly, String currentUser);
+
+    @Update("update hichina_product SET product_type_id=#{productTypeId}, sku_group_id=#{skuGroupId}, product_name=#{productName}, product_content=#{productContent}, created_time=#{createdTime} where sku_id=#{skuId}")
+    void updateBySkuId(HichinaProduct product);
+
+    @Select("<script> select count(*) from hichina_product where <if test='mineOnly'> created_by=#{currentUser} and </if> product_type_id=#{productTypeId} </script>")
+    int countByProductTypeId(String productTypeId, boolean mineOnly, String currentUser);
+
+    @Select("<script> select count(*) from hichina_product <if test='mineOnly'> where created_by=#{currentUser} </if> </script>")
+    int count(boolean mineOnly, String currentUser);
+
+
+}

+ 32 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/OrderMapper.java

@@ -0,0 +1,32 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.OrderDetailDTO;
+import com.hichina.admin.hichinaadminbackend.model.DTO.OrderListDTO;
+import com.hichina.admin.hichinaadminbackend.model.Order;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "orderMapper")
+public interface OrderMapper {
+
+//            status
+    @Select("select count(*) from `order`")
+    int count();
+
+    @Select("select count(*) from `order` o inner join hichina_product p on p.sku_id=o.product_sku_id inner join hichina_product_type pt on pt.product_type_id=p.product_type_id where p.product_name like CONCAT('%',CONCAT(#{query},'%')) or o.user_id like CONCAT('%',CONCAT(#{query},'%')) order by o.created_time desc")
+    int countByQuery(String query);
+
+
+    @Select("select o.order_id, o.user_id, p.product_name, p.sku_id, p.product_type_id, pt.product_type_name, o.created_time, o.status, o.price from `order` o inner join hichina_product p on p.sku_id=o.product_sku_id inner join hichina_product_type pt on pt.product_type_id=p.product_type_id order by o.created_time desc")
+    List<OrderListDTO> findAllOrders();
+
+    @Select("select o.order_id, o.user_id, p.product_name, p.sku_id, p.product_type_id, pt.product_type_name, o.created_time, o.status, o.price from `order` o inner join hichina_product p on p.sku_id=o.product_sku_id inner join hichina_product_type pt on pt.product_type_id=p.product_type_id where p.product_name like CONCAT('%',CONCAT(#{query},'%')) or o.user_id like CONCAT('%',CONCAT(#{query},'%')) order by o.created_time desc")
+    List<OrderListDTO> findOrdersByQuery(String query);
+
+    @Select("select o.order_id, o.user_id, p.product_name, p.sku_id, p.product_type_id, pt.product_type_name, o.created_time, o.last_update_time, o.status, o.price, o.meta, o.paying_info, o.remark from `order` o inner join hichina_product p on p.sku_id=o.product_sku_id inner join hichina_product_type pt on pt.product_type_id=p.product_type_id where o.order_id=#{orderId} order by o.created_time desc")
+    List<OrderDetailDTO> findOrderDetailById(String orderId);
+}

+ 35 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductAttributeMapper.java

@@ -0,0 +1,35 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "productAttributeMapper")
+public interface ProductAttributeMapper {
+    @Insert("INSERT INTO product_attribute(attribute_id, attribute_name, data_type) VALUES(#{attributeId},#{attributeName}, #{dataType})")
+    @Options(keyProperty="attribute_id")
+    void insert(ProductAttribute productAttribute);
+
+    @Select("select * from product_attribute where attribute_name like CONCAT('%',CONCAT(#{query},'%'))")
+    List<ProductAttribute> findAttributesByQuery(String query);
+
+    @Select("select count(*) from product_attribute where attribute_name like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countByQuery(String query);
+
+    @Select("select * from product_attribute")
+    List<ProductAttribute> findAllAttributes();
+
+    @Select("select count(*) from product_attribute")
+    Integer count();
+
+    @Delete("<script>" +
+            "Delete FROM product_attribute WHERE attribute_id in \n" +
+            "    <foreach item='item' collection='attributeIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> attributeIds);
+}

+ 82 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuGroupMapper.java

@@ -0,0 +1,82 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.SkuGroupStatsDTO;
+import com.hichina.admin.hichinaadminbackend.model.HichinaLine;
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuGroup;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "productSkuGroupMapper")
+public interface ProductSkuGroupMapper {
+    @Insert("INSERT INTO product_sku_group(sku_group_id, sku_group_name, product_type_id, created_date) VALUES(#{skuGroupId},#{skuGroupName}, #{productTypeId}, #{createdDate})")
+    void insert(ProductSkuGroup productSkuGroup);
+
+    @Update("update product_sku_group SET sku_group_name=#{skuGroupName}, product_type_id=#{productTypeId}, created_date=#{createdDate}, enabled=#{enabled} where sku_group_id=#{skuGroupId}")
+    void update(ProductSkuGroup group);
+
+    @Select("select * from product_sku_group where sku_group_name=#{name} and product_type_id=#{productTypeId}")
+    List<ProductSkuGroup> findByNameAndProductTypeId(String name, String productTypeId);
+
+    @Select("select * from product_sku_group where sku_group_id=#{skuGroupId}")
+    List<ProductSkuGroup> findBySkuGroupId(String skuGroupId);
+
+    @Select("<script> select temp.sku_group_id, temp.sku_group_name, temp.product_type_id, t.product_type_name, temp.product_count, temp.enabled  \n" +
+            "from \n" +
+            "(\n" +
+            "select g.sku_group_id , g.sku_group_name , g.product_type_id , g.enabled, g.created_date, count(p.sku_id) as product_count  \n" +
+            "from product_sku_group g \n" +
+            "inner join hichina_product p on g.sku_group_id = p.sku_group_id <if test='mineOnly'> where p.created_by=#{currentUser}</if> \n" +
+            "group by g.sku_group_id , g.sku_group_name , g.product_type_id, g.enabled, g.created_date\n" +
+            ") temp \n" +
+            "inner join hichina_product_type t on t.product_type_id = temp.product_type_id \n" +
+            "order by temp.created_date desc </script>")
+    List<SkuGroupStatsDTO> findAllSkuGroupStats(boolean mineOnly, String currentUser);
+
+    @Select("<script> select count(*) from\n" +
+            "(\n" +
+            "select temp.sku_group_id, temp.sku_group_name, temp.product_type_id, t.product_type_name, temp.product_count, temp.enabled  \n" +
+            "from \n" +
+            "(\n" +
+            "select g.sku_group_id , g.sku_group_name , g.product_type_id , g.enabled, g.created_date, count(p.sku_id) as product_count  \n" +
+            "from product_sku_group g \n" +
+            "inner join hichina_product p on g.sku_group_id = p.sku_group_id <if test='mineOnly'>where p.created_by=#{currentUser}</if> \n" +
+            "group by g.sku_group_id , g.sku_group_name , g.product_type_id, g.enabled, g.created_date\n" +
+            ") temp \n" +
+            "inner join hichina_product_type t on t.product_type_id = temp.product_type_id \n" +
+            ") cc </script>")
+    Integer countAllSkuGroupStats(boolean mineOnly, String currentUser);
+
+    @Select("<script> select temp.sku_group_id, temp.sku_group_name, temp.product_type_id, t.product_type_name, temp.product_count, temp.enabled  \n" +
+            "from \n" +
+            "(\n" +
+            "select g.sku_group_id , g.sku_group_name , g.product_type_id , g.enabled, g.created_date, count(p.sku_id) as product_count  \n" +
+            "from product_sku_group g \n" +
+            "inner join hichina_product p on g.sku_group_id = p.sku_group_id <if test='mineOnly'>where p.created_by=#{currentUser}</if> \n" +
+            "group by g.sku_group_id , g.sku_group_name , g.product_type_id, g.enabled, g.created_date\n" +
+            ") temp \n" +
+            "inner join hichina_product_type t on t.product_type_id = temp.product_type_id \n" +
+            "where temp.sku_group_name like  CONCAT('%',CONCAT(#{query},'%')) order by temp.created_date desc </script>")
+    List<SkuGroupStatsDTO> findSkuGroupStatsByQuery(String query, boolean mineOnly, String currentUser);
+
+    @Select("<script> select count(*) from\n" +
+            "(\n" +
+            "select temp.sku_group_id, temp.sku_group_name, temp.product_type_id, t.product_type_name, temp.product_count, temp.enabled  \n" +
+            "from \n" +
+            "(\n" +
+            "select g.sku_group_id , g.sku_group_name , g.product_type_id , g.enabled, g.created_date, count(p.sku_id) as product_count  \n" +
+            "from product_sku_group g \n" +
+            "inner join hichina_product p on g.sku_group_id = p.sku_group_id <if test='mineOnly'>where p.created_by=#{currentUser} </if> \n" +
+            "group by g.sku_group_id , g.sku_group_name , g.product_type_id, g.enabled, g.created_date\n" +
+            ") temp \n" +
+            "inner join hichina_product_type t on t.product_type_id = temp.product_type_id \n" +
+            "where temp.sku_group_name like CONCAT('%',CONCAT(#{query},'%'))\n" +
+            ") cc </script>")
+    Integer countSkuGroupStatsByQuery(String query, boolean mineOnly, String currentUser);
+
+    @Delete("delete from product_sku_group where sku_group_id not in (select distinct sku_group_id from hichina_product)")
+    void deleteUnreferenced();
+}

+ 34 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuIntAttributeMappingMapper.java

@@ -0,0 +1,34 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.ProductPropertyBag;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuIntAttributeMapping;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "productSkuIntAttributeMappingMapper")
+public interface ProductSkuIntAttributeMappingMapper {
+    @Insert("INSERT INTO product_sku_int_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue})")
+    @Options(keyProperty="sku_id")
+    void insert(ProductSkuIntAttributeMapping productSkuIntAttributeMapping);
+
+    @Delete("<script>" +
+            "Delete FROM product_sku_int_attribute_mapping WHERE sku_id in \n" +
+            "    <foreach item='item' collection='toBeDel' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> toBeDel);
+
+    @Select("select ptam.`sequence` , a.attribute_name, pm.attribute_id, pm.attribute_value, pm.data_type from product_sku_int_attribute_mapping pm inner join product_attribute a on a.attribute_id=pm.attribute_id inner join hichina_product hp on hp.sku_id = pm.sku_id inner join product_type_attribute_mapping ptam on ptam.product_type_id = hp.product_type_id and ptam.attribute_id = pm.attribute_id where pm.sku_id=#{skuId}")
+    List<ProductPropertyBag> findAttributeValueBySkuId(String skuId);
+
+    @Update("update product_sku_int_attribute_mapping SET attributeValue=#{value} where sku_id=#{skuId} and attribute_id=#{attributeId}")
+    void updateBySkuIdAndAttributeId(String skuId, String attributeId, Integer value);
+
+    @Insert("insert into product_sku_int_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue}) ON DUPLICATE KEY UPDATE attribute_value=#{attributeValue}")
+    void upsertBySkuIdAndAttributeId(ProductSkuIntAttributeMapping obj);
+
+}

+ 35 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuTimestampAttributeMappingMapper.java

@@ -0,0 +1,35 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.ProductPropertyBag;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuTimestampAttributeMapping;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuVarcharAttributeMapping;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+@Mapper
+@Component(value = "productSkuTimestampAttributeMappingMapper")
+public interface ProductSkuTimestampAttributeMappingMapper {
+    @Insert("INSERT INTO product_sku_timestamp_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue})")
+    @Options(keyProperty="sku_id")
+    void insert(ProductSkuTimestampAttributeMapping productSkuTimestampAttributeMapping);
+
+    @Delete("<script>" +
+            "Delete FROM product_sku_timestamp_attribute_mapping WHERE sku_id in \n" +
+            "    <foreach item='item' collection='toBeDel' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> toBeDel);
+
+    @Select("select ptam.`sequence`, a.attribute_name, pm.attribute_id, pm.attribute_value, pm.data_type from product_sku_timestamp_attribute_mapping pm inner join product_attribute a on a.attribute_id=pm.attribute_id inner join hichina_product hp on hp.sku_id = pm.sku_id inner join product_type_attribute_mapping ptam on ptam.product_type_id = hp.product_type_id and ptam.attribute_id = pm.attribute_id where pm.sku_id=#{skuId}")
+    List<ProductPropertyBag> findAttributeValueBySkuId(String skuId);
+
+    @Update("update product_sku_timestamp_attribute_mapping SET attributeValue=#{value} where sku_id=#{skuId} and attribute_id=#{attributeId}")
+    void updateBySkuIdAndAttributeId(String skuId, String attributeId, Date value);
+
+    @Insert("insert into product_sku_timestamp_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue}) ON DUPLICATE KEY UPDATE attribute_value=#{attributeValue}")
+    void upsertBySkuIdAndAttributeId(ProductSkuTimestampAttributeMapping obj);
+}

+ 33 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductSkuVarcharAttributeMappingMapper.java

@@ -0,0 +1,33 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.DTO.ProductPropertyBag;
+import com.hichina.admin.hichinaadminbackend.model.ProductSkuVarcharAttributeMapping;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "productSkuVarcharAttributeMappingMapper")
+public interface ProductSkuVarcharAttributeMappingMapper {
+    @Insert("INSERT INTO product_sku_varchar_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue})")
+    @Options(keyProperty="sku_id")
+    void insert(ProductSkuVarcharAttributeMapping productSkuVarcharAttributeMapping);
+
+    @Delete("<script>" +
+            "Delete FROM product_sku_varchar_attribute_mapping WHERE sku_id in \n" +
+            "    <foreach item='item' collection='toBeDel' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> toBeDel);
+
+    @Select("select ptam.`sequence`, a.attribute_name, pm.attribute_id, pm.attribute_value, pm.data_type from product_sku_varchar_attribute_mapping pm inner join product_attribute a on a.attribute_id=pm.attribute_id inner join hichina_product hp on hp.sku_id = pm.sku_id inner join product_type_attribute_mapping ptam on ptam.product_type_id = hp.product_type_id and ptam.attribute_id = pm.attribute_id where pm.sku_id=#{skuId}")
+    List<ProductPropertyBag> findAttributeValueBySkuId(String skuId);
+
+    @Update("update product_sku_varchar_attribute_mapping SET attributeValue=#{value} where sku_id=#{skuId} and attribute_id=#{attributeId}")
+    void updateBySkuIdAndAttributeId(String skuId, String attributeId, String value);
+
+    @Insert("insert into product_sku_varchar_attribute_mapping(sku_id, data_type, attribute_id, attribute_value) VALUES(#{skuId},#{dataType}, #{attributeId}, #{attributeValue}) ON DUPLICATE KEY UPDATE attribute_value=#{attributeValue}")
+    void upsertBySkuIdAndAttributeId(ProductSkuVarcharAttributeMapping obj);
+}

+ 49 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/ProductTypeMapper.java

@@ -0,0 +1,49 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import com.hichina.admin.hichinaadminbackend.model.ProductType;
+import com.hichina.admin.hichinaadminbackend.model.ProductTypeAttributeMapping;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "productTypeMapper")
+public interface ProductTypeMapper {
+    @Select("select * from hichina_product_type where product_type_name like CONCAT('%',CONCAT(#{query},'%'))")
+    List<ProductType> findProductTypeByQuery(String query);
+
+    @Insert("INSERT INTO product_type_attribute_mapping(attribute_id, product_type_id, sequence) VALUES(#{attributeId},#{productTypeId}, #{sequence})")
+    void bindAttr(ProductTypeAttributeMapping productTypeAttributeMapping);
+
+    @Delete("delete from product_type_attribute_mapping where product_type_id=#{productTypeId} and attribute_id=#{attributeId}")
+    void unbindAttr(String productTypeId, String attributeId);
+
+    @Select("select count(*) from hichina_product_type where product_type_name like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countByQuery(String query);
+
+    @Select("select * from hichina_product_type")
+    List<ProductType> findAllProductType();
+
+    @Select("select att.attribute_id , att.attribute_name , att.data_type  from product_attribute att inner join hichina_product_type tp\n" +
+            "inner join product_type_attribute_mapping mp on mp.product_type_id = tp.product_type_id and mp.attribute_id = att.attribute_id \n" +
+            "where tp.product_type_id  = #{productTypeId} order by mp.sequence")
+    List<ProductAttribute> findAttributesByProductType(String productTypeId);
+
+    @Select("select count(*) from hichina_product_type")
+    Integer count();
+
+    @Insert("INSERT INTO hichina_product_type(product_type_id, product_type_name, product_type_description) VALUES(#{productTypeId},#{productTypeName}, #{productTypeDescription})")
+    @Options(keyProperty="product_type_id")
+    void insert(ProductType productType);
+
+    @Select("<script>" +
+            "Delete FROM hichina_product_type WHERE product_type_id in \n" +
+            "    <foreach item='item' collection='productTypeIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> productTypeIds);
+
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/UserMapper.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.mapper;
+
+import com.hichina.admin.hichinaadminbackend.model.User;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component(value = "userMapper")
+public interface UserMapper {
+    @Select("select * from user where email=#{email}")
+    List<User> findByEmail(String email);
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/AdminUser.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class AdminUser {
+    private String username;
+    private String password;
+    private Date createdTime;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/AdminUserCreateDTO.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class AdminUserCreateDTO {
+    private String username;
+    private String password;
+    private String role;
+}

+ 11 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/BindAtrribute2ProductRequest.java

@@ -0,0 +1,11 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class BindAtrribute2ProductRequest {
+    private String productTypeId;
+    private String attributeId;
+}

+ 16 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/CreateGuidebookDTO.java

@@ -0,0 +1,16 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class CreateGuidebookDTO {
+    private String destinationId;
+
+    private String coverImageUrl;
+
+    private String shortDescription;
+
+    private String downloadUrl;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationBatchDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class DestinationBatchDeleteRequest {
+    private List<String> toDelete;
+}

+ 18 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationCreateDTO.java

@@ -0,0 +1,18 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class DestinationCreateDTO {
+    private String name;
+
+    private String description;
+
+    private Integer level;
+
+    private String destinationProfileImage;
+
+    private String parentId;
+}

+ 18 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/DestinationUpdateDTO.java

@@ -0,0 +1,18 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class DestinationUpdateDTO {
+    private String name;
+
+    private String description;
+
+    private String destinationProfileImage;
+
+    private Integer level;
+
+    private String parentId;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GeneralBatchDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class GeneralBatchDeleteRequest {
+    private List<String> toDelete;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GuidebookDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class GuidebookDeleteRequest {
+    private List<String> toDelete;
+}

+ 20 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/GuidebookListDTO.java

@@ -0,0 +1,20 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class GuidebookListDTO {
+    private String guideId;
+
+    private String downloadUrl;
+
+    private String coverImageUrl;
+
+    private String shortDescription;
+
+    private String destinationId;
+
+    private String destinationName;
+}

+ 27 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaProductDTO.java

@@ -0,0 +1,27 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@NoArgsConstructor
+public class HichinaProductDTO {
+    @JsonProperty("sku_id")
+    private String skuId;
+    @JsonProperty("product_type_name")
+    private String productTypeName;
+    @JsonProperty("product_name")
+    private String productName;
+
+    @JsonProperty("product_type_id")
+    private String productTypeId;
+
+    @JsonProperty("product_content")
+    private String productContent;
+
+    @JsonProperty("created_time")
+    private String createdTime;
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaProductWithPropertyBagDTO.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class HichinaProductWithPropertyBagDTO {
+    private HichinaProductDTO hichinaProductDTO;
+
+    private List<ProductPropertyBag> productPropertyBag;
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/HichinaResponse.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class HichinaResponse {
+    private Boolean ok;
+    private String message;
+    private Object data;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LineBatchDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class LineBatchDeleteRequest {
+    private List<String> toDelete;
+}

+ 19 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LineRequest.java

@@ -0,0 +1,19 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class LineRequest {
+    private String name;
+
+    private String iconPath;
+
+    private Integer rangeInDays;
+
+    private Float basePrice;
+
+    private String description;
+
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/LoginTestDTO.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class LoginTestDTO {
+    private String email;
+
+    private String password;
+}

+ 27 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/OrderDetailDTO.java

@@ -0,0 +1,27 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import com.hichina.admin.hichinaadminbackend.model.Order;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class OrderDetailDTO {
+    private String orderId;
+    private String userId;
+    private String productName;
+    private String skuId;
+    private String productTypeId;
+    private String productTypeName;
+    private Date createdTime;
+    private Date lastUpdateTime;
+    private Order.OrderStatus status;
+    private Integer price;
+    private String meta;
+    private String payingInfo;
+    private String remark;
+
+
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/OrderListDTO.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import com.hichina.admin.hichinaadminbackend.model.Order;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class OrderListDTO {
+    private String orderId;
+    private String userId;
+    private String productName;
+    private String skuId;
+    private String productTypeId;
+    private String productTypeName;
+    private Date createdTime;
+    private Order.OrderStatus status;
+    private Integer price;
+}

+ 22 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/PaginationWrapper.java

@@ -0,0 +1,22 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class PaginationWrapper {
+    private int total;
+    private int pageSize;
+    private int currentPage;
+    private Object data;
+
+    public PaginationWrapper(int total, int pageSize, int currentPage, Object data) {
+        this.total = total;
+        this.pageSize = pageSize;
+        this.currentPage = currentPage;
+        this.data = data;
+    }
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeBatchDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class ProductAttributeBatchDeleteRequest {
+    private List<String> toDelete;
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeCreateDTO.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class ProductAttributeCreateDTO {
+    private String name;
+
+    private String dataType;
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductAttributeTypeShortDTO.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductAttributeTypeShortDTO {
+    private String attributeId;
+
+    private String attributeName;
+
+    private String dataType;
+}

+ 20 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductPropertyBag.java

@@ -0,0 +1,20 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductPropertyBag {
+    private Long sequence;
+
+    private String attributeId;
+
+    private String attributeName;
+
+    private String attributeValue;
+
+    @NotNull
+    private String dataType;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuBatchDeleteRequest.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class ProductSkuBatchDeleteRequest {
+    private List<String> toDelete;
+}

+ 20 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuCreateDTO.java

@@ -0,0 +1,20 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Map;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class ProductSkuCreateDTO {
+    private String productName;
+
+    private String productDescription;
+
+    private String productTypeId;
+
+    private Map<String, String> customPropertyBag;
+}

+ 17 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductSkuUpdateDTO.java

@@ -0,0 +1,17 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class ProductSkuUpdateDTO {
+    private String productName;
+
+    private String productContent;
+
+    private List<ProductPropertyBag> propertyBags;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductTypeBatchDeleteRequestDTO.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+public class ProductTypeBatchDeleteRequestDTO {
+    private List<String> toDelete;
+}

+ 13 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/ProductTypeCreateDTO.java

@@ -0,0 +1,13 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class ProductTypeCreateDTO {
+    private String name;
+    private String description;
+}

+ 23 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/SkuGroupStatsDTO.java

@@ -0,0 +1,23 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class SkuGroupStatsDTO {
+    private String skuGroupId;
+
+    private String skuGroupName;
+
+    private String productTypeId;
+
+    private String productTypeName;
+
+    private Integer productCount;
+
+    private Boolean enabled;
+}

+ 27 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Destination.java

@@ -0,0 +1,27 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class Destination {
+    private String destinationId;
+
+    private String destinationName;
+
+    // 5, 6, 7 分别代表省,市,景点三级
+    private Integer level;
+
+    private String parentId;
+
+    private String description;
+
+    private String destinationProfileImage;
+
+    @JsonProperty("created_date")
+    private Date createdDate;
+}

+ 22 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Guidebook.java

@@ -0,0 +1,22 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class Guidebook {
+    private String guideId;
+
+    private String downloadUrl;
+
+    private String coverImageUrl;
+
+    private String shortDescription;
+
+    private String destinationId;
+
+    private Date createdDate;
+}

+ 35 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/HichinaLine.java

@@ -0,0 +1,35 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class HichinaLine {
+    private Long id;
+
+    @JsonProperty("public_id")
+
+    private String publicId;
+
+    @NotNull
+    private String name;
+
+    @JsonProperty("created_date")
+    private Date createdDate;
+
+    @JsonProperty("icon_path")
+    private String iconPath;
+
+    @JsonProperty("range_in_days")
+    private Integer rangeInDays;
+
+    @JsonProperty("base_price")
+    private float basePrice;
+
+    private String description;
+}

+ 25 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/HichinaProduct.java

@@ -0,0 +1,25 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class HichinaProduct {
+    private String skuId;
+
+    private String skuGroupId;
+
+    private String productTypeId;
+
+    private String productName;
+
+    private String productContent;
+
+    private Date createdTime;
+
+    private String createdBy;
+
+}

+ 28 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Order.java

@@ -0,0 +1,28 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class Order {
+    private String orderId;
+    private String userId;
+    private String productSkuId;
+    private Date createdTime;
+    private Date lastUpdateTime;
+    private String meta;
+    private OrderStatus status;
+    private String payingInfo;
+    private String remark;
+    private Integer price;
+
+    public enum OrderStatus {
+        SUBMITTED,
+        PAID,
+        DELETED,
+        FINISHED;
+    }
+}

+ 19 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductAttribute.java

@@ -0,0 +1,19 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductAttribute {
+    @NotNull
+    private String attributeId;
+
+    @NotNull
+    private String attributeName;
+
+    @NotNull
+    private String dataType;
+}

+ 22 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuGroup.java

@@ -0,0 +1,22 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ProductSkuGroup {
+    private String skuGroupId;
+
+    private String skuGroupName;
+
+    private String productTypeId;
+
+    private Date createdDate;
+
+    private Boolean enabled;
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuIntAttributeMapping.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductSkuIntAttributeMapping {
+    @JsonProperty("sku_id")
+    private String skuId;
+
+    @JsonProperty("data_type")
+    private String dataType;
+
+    @JsonProperty("attribute_id")
+    private String attributeId;
+
+    @JsonProperty("attribute_value")
+    private Integer attributeValue;
+}

+ 23 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuTimestampAttributeMapping.java

@@ -0,0 +1,23 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class ProductSkuTimestampAttributeMapping {
+    @JsonProperty("sku_id")
+    private String skuId;
+
+    @JsonProperty("data_type")
+    private String dataType;
+
+    @JsonProperty("attribute_id")
+    private String attributeId;
+
+    @JsonProperty("attribute_value")
+    private Date attributeValue;
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductSkuVarcharAttributeMapping.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductSkuVarcharAttributeMapping {
+    @JsonProperty("sku_id")
+    private String skuId;
+
+    @JsonProperty("data_type")
+    private String dataType;
+
+    @JsonProperty("attribute_id")
+    private String attributeId;
+
+    @JsonProperty("attribute_value")
+    private String attributeValue;
+}

+ 21 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductType.java

@@ -0,0 +1,21 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class ProductType {
+    @NotNull
+    @JsonProperty("product_type_id")
+    private String productTypeId;
+
+    @NotNull
+    @JsonProperty("product_type_name")
+    private String productTypeName;
+
+    @JsonProperty("product_type_description")
+    private String productTypeDescription;
+}

+ 19 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/ProductTypeAttributeMapping.java

@@ -0,0 +1,19 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * this is a many to many relationship relation table
+ */
+@Data
+@NoArgsConstructor
+public class ProductTypeAttributeMapping {
+    private String productTypeId;
+    private String attributeId;
+
+    /**
+     * this is to maintain order of all attributes for a specific product type
+     */
+    private String sequence;
+}

+ 33 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/User.java

@@ -0,0 +1,33 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class User {
+    private String userId;
+    private String salt;
+    private String password;
+    private Date createdTime;
+    private String email;
+    private String facebookId;
+    private String googleId;
+    private String wxId;
+    private String phone;
+    private String username;
+    private Integer gender;
+    private Date birthDate;
+    private String nationality;
+    private String licenseType;
+    private String licenseNumber;
+    private Date licenseSignDate;
+    private Date licenseExpireDate;
+    private String signature;
+    private String profileImageUrl;
+    private String passportImageUrl;
+    private Integer pwdCode;
+
+}

+ 17 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/BlogSlideImage.java

@@ -0,0 +1,17 @@
+package com.hichina.admin.hichinaadminbackend.model.mongo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document("BlogSlideImage")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlogSlideImage {
+    private String title;
+    private String subTitle;
+    private String imageUrl;
+    private String linkToBlog;
+}

+ 15 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/GuidebookIntroSlideImage.java

@@ -0,0 +1,15 @@
+package com.hichina.admin.hichinaadminbackend.model.mongo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document("GuidebookIntroSlideImage")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class GuidebookIntroSlideImage {
+    private String imageUrl;
+    private String linkTo;
+}

+ 17 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/mongo/HomeSlideImage.java

@@ -0,0 +1,17 @@
+package com.hichina.admin.hichinaadminbackend.model.mongo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document("HomeSlideImage")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class HomeSlideImage {
+    private String title;
+    private String subTitle;
+    private String imageUrl;
+    private String linkToBlog;
+}

+ 12 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/BlogSlideImageRepository.java

@@ -0,0 +1,12 @@
+package com.hichina.admin.hichinaadminbackend.repository;
+
+import com.hichina.admin.hichinaadminbackend.model.mongo.BlogSlideImage;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.List;
+
+public interface BlogSlideImageRepository extends MongoRepository<BlogSlideImage, String> {
+    List<BlogSlideImage> findAll();
+
+    public long count();
+}

+ 7 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/GuidebookIntroSlideImageRepository.java

@@ -0,0 +1,7 @@
+package com.hichina.admin.hichinaadminbackend.repository;
+
+import com.hichina.admin.hichinaadminbackend.model.mongo.GuidebookIntroSlideImage;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+public interface GuidebookIntroSlideImageRepository  extends MongoRepository<GuidebookIntroSlideImage, String> {
+}

+ 15 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/repository/HomeSlideImageRepository.java

@@ -0,0 +1,15 @@
+package com.hichina.admin.hichinaadminbackend.repository;
+
+import com.hichina.admin.hichinaadminbackend.model.mongo.HomeSlideImage;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.mongodb.repository.Query;
+
+import java.util.List;
+
+public interface HomeSlideImageRepository extends MongoRepository<HomeSlideImage, String> {
+
+    List<HomeSlideImage> findAll();
+
+    public long count();
+
+}

+ 32 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/schedule/ScheduledJobs.java

@@ -0,0 +1,32 @@
+package com.hichina.admin.hichinaadminbackend.schedule;
+
+import com.hichina.admin.hichinaadminbackend.controller.ImageController;
+import com.hichina.admin.hichinaadminbackend.mapper.BlogMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Calendar;
+import java.util.Date;
+
+@Component
+public class ScheduledJobs {
+    private static final Logger LOG = LoggerFactory.getLogger(ScheduledJobs.class);
+
+    @Autowired
+    private BlogMapper blogMapper;
+
+    @Scheduled(cron = "0 0/30 * * * ?")
+    public void UpdateBlogProfileImage(){
+        LOG.info("===executing update blog profile image");
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.HOUR, -1  );
+        blogMapper.updateCoverWithFirstImage(calendar.getTime());
+    }
+
+}

+ 164 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/service/InitDataService.java

@@ -0,0 +1,164 @@
+package com.hichina.admin.hichinaadminbackend.service;
+
+import com.hichina.admin.hichinaadminbackend.config.Constants;
+import com.hichina.admin.hichinaadminbackend.mapper.AdminUserMapper;
+import com.hichina.admin.hichinaadminbackend.mapper.ProductAttributeMapper;
+import com.hichina.admin.hichinaadminbackend.mapper.ProductTypeMapper;
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import com.hichina.admin.hichinaadminbackend.model.ProductAttribute;
+import com.hichina.admin.hichinaadminbackend.model.ProductType;
+import com.hichina.admin.hichinaadminbackend.model.ProductTypeAttributeMapping;
+import com.hichina.admin.hichinaadminbackend.util.AtomicSequenceGenerator;
+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 java.util.Date;
+
+@Service
+public class InitDataService {
+    private static final Logger LOG = LoggerFactory.getLogger(InitDataService.class);
+
+    @Autowired
+    private ProductAttributeMapper productAttributeMapper;
+
+    private static AtomicSequenceGenerator sequenceGenerator = AtomicSequenceGenerator.getInstance();
+
+    @Autowired
+    private ProductTypeMapper productTypeMapper;
+
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+
+    private void insertPropertyIfNotExist(String name, String id, String dataType){
+        ProductAttribute productAttribute = new ProductAttribute();
+        productAttribute.setAttributeName(name);
+        productAttribute.setAttributeId(id);
+        productAttribute.setDataType(dataType);
+        try{
+            productAttributeMapper.insert(productAttribute);
+        }catch (Exception e){
+            LOG.info("Not inserted attribute at startup: "+e.getMessage());
+        }
+    }
+
+    private void insertProductTypeIfNotExist(String name, String id,  String description){
+        ProductType productType = new ProductType();
+        productType.setProductTypeName(name);
+        productType.setProductTypeDescription(description);
+        productType.setProductTypeId(id);
+
+        try{
+            productTypeMapper.insert(productType);
+        }catch (Exception e){
+            LOG.info("Not inserted product type at startup: "+e.getMessage());
+        }
+    }
+
+    private void bindAttributeToProductType(String attrId, String productTypeId){
+        ProductTypeAttributeMapping productTypeAttributeMapping = new ProductTypeAttributeMapping();
+
+        productTypeAttributeMapping.setAttributeId(attrId);
+        productTypeAttributeMapping.setProductTypeId(productTypeId);
+        productTypeAttributeMapping.setSequence(String.valueOf(sequenceGenerator.getNext()));
+
+        try{
+            productTypeMapper.bindAttr(productTypeAttributeMapping);
+        }catch (Exception e){
+            LOG.info(String.format("Fail to bind attribute with producttype %s-$s, at startup: ",attrId, productTypeId)+e.getMessage());
+        }
+    }
+
+    public void initAdminUser(){
+        try{
+            AdminUser adminUser = new AdminUser();
+            adminUser.setUsername("hichinaadmin");
+            adminUser.setPassword("DtYV5/9L");
+            adminUser.setCreatedTime(new Date());
+            adminUserMapper.insert(adminUser);
+        }catch (Exception e){
+            LOG.info("Super admin user already exist:  "+e.getMessage());
+        }
+    }
+
+    @Transactional
+    public void initProperty(){
+        LOG.info("executing insertInitProperty");
+
+        insertPropertyIfNotExist("产品图", Constants.PRODUCTPROFILEIMAGEPROP, "image");
+        insertPropertyIfNotExist("可用日期", Constants.AVAILABLEDATEGUIDPROP, "datestring");
+        insertPropertyIfNotExist("天数", Constants.DAYSPROP, "integer");
+        insertPropertyIfNotExist("供应商", Constants.SUPPLIERPROP, "string");
+        insertPropertyIfNotExist("Option", Constants.TYPEOFPACKAGEPROP, "string");
+        insertPropertyIfNotExist("成人单价", Constants.ADULTPRICEPROP, "integer");
+        insertPropertyIfNotExist("儿童单价(>=2岁)", Constants.CHILDPRICEPROP, "integer");
+        insertPropertyIfNotExist("婴儿单价(<2岁)", Constants.INFANTPRICEPROP, "integer");
+        insertPropertyIfNotExist("单房差", Constants.SINGLEDIFFPRICEPROP , "integer");
+        insertPropertyIfNotExist("价格", Constants.GENERALPRICE , "integer");
+        insertPropertyIfNotExist("最大数量", Constants.MAXNUM , "integer");
+    }
+
+    @Transactional
+    public void initProductType(){
+        LOG.info("executed initProductType");
+        insertProductTypeIfNotExist("跟团游", Constants.TOURPRODUCTTYPE, "跟团游");
+        insertProductTypeIfNotExist("机酒自由行", Constants.FLIGHTHOTELPRODUCTTYPE, "机酒自由行");
+        insertProductTypeIfNotExist("机票package", Constants.FLIGHTPRODUCTTYPE, "机票package");
+        insertProductTypeIfNotExist("酒店package", Constants.HOTELPRODUCTTYPE, "酒店package");
+        insertProductTypeIfNotExist("文创土特产", Constants.LOCALSPECIALTYPRODUCTTYPE, "文创土特产");
+    }
+
+    @Transactional
+    public void initProductTypeBinding(){
+        LOG.info("Executing initProductTypeBinding");
+
+        // 跟团游
+        bindAttributeToProductType(Constants.PRODUCTPROFILEIMAGEPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.AVAILABLEDATEGUIDPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.DAYSPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SUPPLIERPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.TYPEOFPACKAGEPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.ADULTPRICEPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.CHILDPRICEPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.INFANTPRICEPROP,Constants.TOURPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SINGLEDIFFPRICEPROP,Constants.TOURPRODUCTTYPE);
+
+        //机酒自由行
+        bindAttributeToProductType(Constants.PRODUCTPROFILEIMAGEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.AVAILABLEDATEGUIDPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.DAYSPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SUPPLIERPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.TYPEOFPACKAGEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.ADULTPRICEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.CHILDPRICEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.INFANTPRICEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SINGLEDIFFPRICEPROP,Constants.FLIGHTHOTELPRODUCTTYPE);
+
+        //机票package
+        bindAttributeToProductType(Constants.PRODUCTPROFILEIMAGEPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.AVAILABLEDATEGUIDPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.DAYSPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SUPPLIERPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.TYPEOFPACKAGEPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.ADULTPRICEPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.CHILDPRICEPROP,Constants.FLIGHTPRODUCTTYPE);
+        bindAttributeToProductType(Constants.INFANTPRICEPROP,Constants.FLIGHTPRODUCTTYPE);
+
+        //酒店package
+        bindAttributeToProductType(Constants.PRODUCTPROFILEIMAGEPROP,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.AVAILABLEDATEGUIDPROP,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.DAYSPROP,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SUPPLIERPROP,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.TYPEOFPACKAGEPROP,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.GENERALPRICE,Constants.HOTELPRODUCTTYPE);
+        bindAttributeToProductType(Constants.MAXNUM,Constants.HOTELPRODUCTTYPE);
+
+        //文创土特产
+        bindAttributeToProductType(Constants.PRODUCTPROFILEIMAGEPROP,Constants.LOCALSPECIALTYPRODUCTTYPE);
+        bindAttributeToProductType(Constants.SUPPLIERPROP,Constants.LOCALSPECIALTYPRODUCTTYPE);
+        bindAttributeToProductType(Constants.TYPEOFPACKAGEPROP,Constants.LOCALSPECIALTYPRODUCTTYPE);
+        bindAttributeToProductType(Constants.GENERALPRICE,Constants.LOCALSPECIALTYPRODUCTTYPE);
+    }
+}

+ 26 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/AtomicSequenceGenerator.java

@@ -0,0 +1,26 @@
+package com.hichina.admin.hichinaadminbackend.util;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+
+public class AtomicSequenceGenerator implements SequenceGenerator {
+
+    private AtomicLong value = new AtomicLong(1);
+
+    @Override
+    public long getNext() {
+        return System.currentTimeMillis() + value.getAndIncrement();
+    }
+
+
+    private static AtomicSequenceGenerator instance;
+
+    private AtomicSequenceGenerator(){}
+
+    public static synchronized AtomicSequenceGenerator getInstance() {
+        if (instance == null) {
+            instance = new AtomicSequenceGenerator();
+        }
+        return instance;
+    }
+}

+ 5 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/SequenceGenerator.java

@@ -0,0 +1,5 @@
+package com.hichina.admin.hichinaadminbackend.util;
+
+public interface SequenceGenerator {
+    long getNext();
+}

+ 36 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/util/UserUtil.java

@@ -0,0 +1,36 @@
+package com.hichina.admin.hichinaadminbackend.util;
+
+import com.hichina.admin.hichinaadminbackend.mapper.AdminUserMapper;
+import com.hichina.admin.hichinaadminbackend.model.AdminUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class UserUtil {
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+
+    public String currentUserName(){
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)) {
+            String currentUserName = authentication.getName();
+            return currentUserName;
+        }else{
+            return null;
+        }
+    }
+
+    public AdminUser currentUser(String username){
+        List<AdminUser> adminUsers = adminUserMapper.getUserByName(username);
+        if(adminUsers.isEmpty()){
+            return null;
+        }else{
+            return adminUsers.get(0);
+        }
+    }
+}

Some files were not shown because too many files changed in this diff