523096025 7 місяців тому
коміт
7e975c5f3b
100 змінених файлів з 11813 додано та 0 видалено
  1. 34 0
      .gitignore
  2. 1 0
      huimv-farm/README.md
  3. 154 0
      huimv-farm/pom.xml
  4. 63 0
      huimv-farm/src/main/java/vip/xiaonuo/Application.java
  5. 92 0
      huimv-farm/src/main/java/vip/xiaonuo/core/config/BaseConfigure.java
  6. 76 0
      huimv-farm/src/main/java/vip/xiaonuo/core/config/DruidConfigure.java
  7. 644 0
      huimv-farm/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java
  8. 20 0
      huimv-farm/src/main/java/vip/xiaonuo/core/converter/MultipartJackson2HttpMessageConverter.java
  9. 37 0
      huimv-farm/src/main/java/vip/xiaonuo/core/enums/BaseDataTypeEnum.java
  10. 18 0
      huimv-farm/src/main/java/vip/xiaonuo/core/enums/EnvDataTypeEnum.java
  11. 75 0
      huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorAttributesHandler.java
  12. 67 0
      huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorViewController.java
  13. 73 0
      huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorViewHandler.java
  14. 40 0
      huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalExceptionHandler.java
  15. 190 0
      huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalExceptionUtil.java
  16. 14 0
      huimv-farm/src/main/java/vip/xiaonuo/core/param/OrgIdParam.java
  17. 67 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/config/HrConfigure.java
  18. 131 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/controller/HrBaseEmployeeInfoController.java
  19. 159 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/entity/HrBaseEmployeeInfo.java
  20. 34 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/enums/HrBaseEmployeeInfoEnum.java
  21. 25 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/mapper/HrBaseEmployeeInfoMapper.java
  22. 5 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/mapper/mapping/HrBaseEmployeeInfoMapper.xml
  23. 127 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoAddParam.java
  24. 133 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoEditParam.java
  25. 35 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoIdParam.java
  26. 49 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoPageParam.java
  27. 80 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/service/HrBaseEmployeeInfoService.java
  28. 94 0
      huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/service/impl/HrBaseEmployeeInfoServiceImpl.java
  29. 6 0
      huimv-farm/src/main/resources/README.md
  30. 1048 0
      huimv-farm/src/main/resources/_sql/snowy_mysql.sql
  31. 1891 0
      huimv-farm/src/main/resources/_sql/snowy_oracle.sql
  32. 3 0
      huimv-farm/src/main/resources/_sql/数据库说明.md
  33. 183 0
      huimv-farm/src/main/resources/application-test.properties
  34. 190 0
      huimv-farm/src/main/resources/application.properties
  35. 118 0
      huimv-farm/src/main/resources/logback-spring.xml
  36. 34 0
      huimv-farm/src/test/java/vip/xiaonuo/MainTest.java
  37. 25 0
      huimv-farm/src/test/java/vip/xiaonuo/Test.java
  38. 707 0
      pom.xml
  39. 1 0
      snowy-common/READM.md
  40. 182 0
      snowy-common/pom.xml
  41. 32 0
      snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonLog.java
  42. 32 0
      snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonNoRepeat.java
  43. 35 0
      snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonWrapper.java
  44. 13 0
      snowy-common/src/main/java/vip/xiaonuo/common/annotation/Test.java
  45. 86 0
      snowy-common/src/main/java/vip/xiaonuo/common/cache/CommonCacheOperator.java
  46. 31 0
      snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonDeleteFlagEnum.java
  47. 42 0
      snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonExceptionEnum.java
  48. 45 0
      snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonSortOrderEnum.java
  49. 112 0
      snowy-common/src/main/java/vip/xiaonuo/common/excel/CommonExcelCustomMergeStrategy.java
  50. 50 0
      snowy-common/src/main/java/vip/xiaonuo/common/exception/CommonException.java
  51. 61 0
      snowy-common/src/main/java/vip/xiaonuo/common/handler/CommonSm4CbcTypeHandler.java
  52. 175 0
      snowy-common/src/main/java/vip/xiaonuo/common/listener/CommonDataChangeEventCenter.java
  53. 76 0
      snowy-common/src/main/java/vip/xiaonuo/common/listener/CommonDataChangeListener.java
  54. 79 0
      snowy-common/src/main/java/vip/xiaonuo/common/page/CommonPageRequest.java
  55. 63 0
      snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonEntity.java
  56. 160 0
      snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonResult.java
  57. 146 0
      snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonValidList.java
  58. 32 0
      snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonWrapperInterface.java
  59. 37 0
      snowy-common/src/main/java/vip/xiaonuo/common/prop/CommonProperties.java
  60. 31 0
      snowy-common/src/main/java/vip/xiaonuo/common/sse/CommonSseParam.java
  61. 30 0
      snowy-common/src/main/java/vip/xiaonuo/common/timer/CommonTimerTaskRunner.java
  62. 126 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonAvatarUtil.java
  63. 142 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonCryptogramUtil.java
  64. 63 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonDownloadUtil.java
  65. 50 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonEmailUtil.java
  66. 40 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonFilterExceptionUtil.java
  67. 108 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonIpAddressUtil.java
  68. 72 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonJoinPointUtil.java
  69. 125 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonNetWorkInfoUtil.java
  70. 65 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonResponseUtil.java
  71. 99 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonServletUtil.java
  72. 183 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonTimeFormatUtil.java
  73. 80 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/CommonUaUtil.java
  74. 184 0
      snowy-common/src/main/java/vip/xiaonuo/common/util/UploadImage.java
  75. BIN
      snowy-common/src/main/resources/ip2region.xdb
  76. 15 0
      snowy-plugin-api/README.md
  77. 38 0
      snowy-plugin-api/pom.xml
  78. 1 0
      snowy-plugin-api/snowy-plugin-auth-api/README.md
  79. 30 0
      snowy-plugin-api/snowy-plugin-auth-api/pom.xml
  80. 126 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java
  81. 35 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckLogin.java
  82. 51 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckPermission.java
  83. 52 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckRole.java
  84. 49 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/enums/SaClientTypeEnum.java
  85. 233 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseClientLoginUser.java
  86. 278 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseLoginUser.java
  87. 47 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpClientLoginUserUtil.java
  88. 936 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpClientUtil.java
  89. 55 0
      snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpLoginUserUtil.java
  90. 1 0
      snowy-plugin-api/snowy-plugin-biz-api/README.md
  91. 24 0
      snowy-plugin-api/snowy-plugin-biz-api/pom.xml
  92. 13 0
      snowy-plugin-api/snowy-plugin-biz-api/src/main/java/vip/xiaonuo/biz/package-info.java
  93. 1 0
      snowy-plugin-api/snowy-plugin-client-api/README.md
  94. 24 0
      snowy-plugin-api/snowy-plugin-client-api/pom.xml
  95. 13 0
      snowy-plugin-api/snowy-plugin-client-api/src/main/java/vip/xiaonuo/client/package-info.java
  96. 1 0
      snowy-plugin-api/snowy-plugin-dev-api/README.md
  97. 108 0
      snowy-plugin-api/snowy-plugin-dev-api/pom.xml
  98. 30 0
      snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevConfigApi.java
  99. 22 0
      snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevDictApi.java
  100. 0 0
      snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevEmailApi.java

+ 34 - 0
.gitignore

@@ -0,0 +1,34 @@
+*.class
+
+# Package Files #
+*.jar
+*.war
+*.ear
+*/target/
+
+# eclipse
+.settings/
+.classpath
+.project
+logs/
+
+# idea
+.idea/
+*.iml
+.murphy.yml
+
+*velocity.log*
+
+### STS ###
+.apt_generated
+.factorypath
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.ipr
+*.log
+tmp/
+!DmJdbcDriver18.jar
+!kingbase8-8.6.0.jar

+ 1 - 0
huimv-farm/README.md

@@ -0,0 +1 @@
+# 主启动模块

+ 154 - 0
huimv-farm/pom.xml

@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>huimv.garden</groupId>
+        <artifactId>garden</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>huimv-garden</artifactId>
+    <!--    <artifactId>huimv-farm</artifactId>-->
+    <packaging>jar</packaging>
+    <description>主启动模块</description>
+
+    <dependencies>
+
+        <!-- test -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- junit -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!--        <dependency>-->
+        <!--            <groupId>org.yaml</groupId>-->
+        <!--            <artifactId>snakeyaml</artifactId>-->
+        <!--            <version>2.2</version>-->
+        <!--        </dependency>-->
+
+        <!-- dynamic-datasource -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- mysql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <!-- postgresql -->
+        <!--<dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>-->
+
+        <!-- oracle -->
+        <!--<dependency>
+            <groupId>com.oracle.database.jdbc</groupId>
+            <artifactId>ojdbc8</artifactId>
+        </dependency>-->
+        <!--<dependency>
+            <groupId>com.oracle.database.nls</groupId>
+            <artifactId>orai18n</artifactId>
+        </dependency>-->
+
+        <!-- mssql -->
+        <!--<dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+        </dependency>-->
+
+        <!-- 达梦数据库 -->
+        <!--<dependency>
+            <groupId>com.dameng</groupId>
+            <artifactId>DmJdbcDriver18</artifactId>
+        </dependency>-->
+
+        <!-- 人大金仓数据库 -->
+        <!--<dependency>
+            <groupId>cn.com.kingbase</groupId>
+            <artifactId>kingbase8</artifactId>
+        </dependency>-->
+
+        <!-- 登录鉴权插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-auth</artifactId>
+        </dependency>
+
+        <!-- 业务功能插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-biz</artifactId>
+        </dependency>
+
+        <!-- C端功能插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-client</artifactId>
+        </dependency>
+
+        <!-- 开发工具插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-dev</artifactId>
+        </dependency>
+
+        <!-- 代码生成插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-gen</artifactId>
+        </dependency>
+
+        <!-- 移动端管理插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-mobile</artifactId>
+        </dependency>
+
+        <!-- 系统功能插件 -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-plugin-sys</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.54</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.5.12</version>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 63 - 0
huimv-farm/src/main/java/vip/xiaonuo/Application.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.Banner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
+
+/**
+ * SpringBoot方式启动类
+ *
+ * @author xuyuxiang
+ * @date 2021/12/18 16:57
+ */
+@Slf4j
+@EnableSwagger2WebMvc
+@RestController
+@SpringBootApplication
+public class Application {
+
+    /* 解决druid 日志报错:discard long time none received connection:xxx */
+    static {
+        System.setProperty("druid.mysql.usePingMethod","false");
+    }
+
+    /**
+     * 主启动函数
+     *
+     * @author xuyuxiang
+     * @date 2022/7/30 21:42
+     */
+    public static void main(String[] args) {
+        SpringApplication springApplication = new SpringApplication(Application.class);
+        springApplication.setBannerMode(Banner.Mode.OFF);
+        springApplication.run(args);
+        log.info(">>> {}", Application.class.getSimpleName().toUpperCase() + " STARTING SUCCESS");
+    }
+
+    /**
+     * 首页
+     *
+     * @author xuyuxiang
+     * @date 2022/7/8 14:22
+     **/
+    @GetMapping("/")
+    public String index() {
+        return "WELCOME";
+    }
+}

+ 92 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/config/BaseConfigure.java

@@ -0,0 +1,92 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.config;
+
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.bind.annotation.RequestMethod;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import vip.xiaonuo.common.pojo.CommonResult;
+
+import javax.annotation.Resource;
+
+/**
+ * 业务相关配置
+ *
+ * @author xuyuxiang
+ * @date 2022/7/7 16:18
+ **/
+@Configuration
+public class BaseConfigure {
+
+    @Resource
+    private OpenApiExtensionResolver openApiExtensionResolver;
+
+    /**
+     * API文档分组配置
+     *
+     * @author xuyuxiang
+     * @date 2022/7/7 16:18
+     **/
+    @Bean(value = "baseDocApi")
+    public Docket baseDocApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(new ApiInfoBuilder()
+                        .title("基础管理")
+                        .description("基础管理")
+                        .termsOfServiceUrl("https://www.baiduc.com")
+                        .contact(new Contact("HUIMV","https://www.baiduc.com", "xuyuxiang29@foxmail.com"))
+                        .version("2.0.0")
+                        .build())
+                .globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
+                .globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
+                .groupName("基础管理")
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .apis(RequestHandlerSelectors.basePackage("vip.xiaonuo.modular.base"))
+                .paths(PathSelectors.any())
+                .build().extensions(openApiExtensionResolver.buildExtensions("基础管理"));
+    }
+
+
+    @Bean(value = "biDocApi")
+    public Docket biDocApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(new ApiInfoBuilder()
+                        .title("BI驾驶舱系统")
+                        .description("BI驾驶舱系统")
+                        .termsOfServiceUrl("https://www.baiduc.com")
+                        .contact(new Contact("HUIMV","https://www.baiduc.com", "xuyuxiang29@foxmail.com"))
+                        .version("2.0.0")
+                        .build())
+                .globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
+                .globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
+                .groupName("BI驾驶舱系统")
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .apis(RequestHandlerSelectors.basePackage("vip.xiaonuo.modular.bi"))
+                .paths(PathSelectors.any())
+                .build().extensions(openApiExtensionResolver.buildExtensions("BI驾驶舱系统"));
+    }
+
+
+
+
+}

+ 76 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/config/DruidConfigure.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.config;
+
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import javax.servlet.*;
+import java.io.IOException;
+
+/**
+ * druid配置
+ *
+ * @author diantu
+ * @date 2023/06/30
+ **/
+@Configuration
+public class DruidConfigure {
+
+    /**
+     * 去除druid监控页面广告
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties)
+    {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter()
+        {
+            @Override
+            public void init(FilterConfig filterConfig) throws ServletException
+            {
+            }
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                    throws IOException, ServletException
+            {
+                chain.doFilter(request, response);
+                // 重置缓冲区,响应头不会被重置
+                response.resetBuffer();
+                // 获取common.js
+                String text = Utils.readFromResource(filePath);
+                // 正则替换banner, 除去底部的广告信息
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+            @Override
+            public void destroy()
+            {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}

+ 644 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/config/GlobalConfigure.java

@@ -0,0 +1,644 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.config;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaResponse;
+import cn.dev33.satoken.filter.SaServletFilter;
+import cn.dev33.satoken.router.SaHttpMethod;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.EnumUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.ContentType;
+import cn.hutool.http.Header;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import vip.xiaonuo.auth.core.util.StpClientUtil;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.core.handler.GlobalExceptionUtil;
+import org.apache.ibatis.mapping.DatabaseIdProvider;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.ReflectionException;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.jdbc.support.JdbcUtils;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import vip.xiaonuo.common.annotation.CommonNoRepeat;
+import vip.xiaonuo.common.annotation.CommonWrapper;
+import vip.xiaonuo.common.cache.CommonCacheOperator;
+import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
+import vip.xiaonuo.common.listener.CommonDataChangeListener;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonWrapperInterface;
+import vip.xiaonuo.common.util.CommonTimeFormatUtil;
+import vip.xiaonuo.sys.core.enums.SysBuildInEnum;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * Snowy配置
+ *
+ * @author xuyuxiang
+ * @date 2021/10/9 14:24
+ **/
+@Configuration
+@MapperScan(basePackages = {"vip.xiaonuo.**.mapper"})
+public class GlobalConfigure implements WebMvcConfigurer {
+
+    private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:";
+
+    @Resource
+    private CommonCacheOperator commonCacheOperator;
+
+    /**
+     * 无需登录的接口地址集合
+     */
+    private static final String[] NO_LOGIN_PATH_ARR = {
+            /* 主入口 */
+            "/",
+
+            /* 静态资源 */
+            "/favicon.ico",
+            "/doc.html",
+            "/webjars/**",
+            "/swagger-resources/**",
+            "/v2/api-docs",
+            "/v2/api-docs-ext",
+            "/configuration/ui",
+            "/configuration/security",
+            "/ureport/**",
+            "/druid/**",
+            "/images/**",
+
+            /* 认证相关 */
+            "/auth/c/getPicCaptcha",
+            "/auth/c/getPhoneValidCode",
+            "/auth/c/doLogin",
+            "/auth/c/doLoginByPhone",
+
+            "/auth/b/getPicCaptcha",
+            "/auth/b/getPhoneValidCode",
+            "/auth/b/doLogin",
+            "/auth/b/doLoginByPhone",
+
+            /* 三方登录相关 */
+            "/auth/third/render",
+            "/auth/third/callback",
+
+            /* 系统基础配置 */
+            "/dev/config/sysBaseList",
+
+            /* 系统字典树 */
+            "/dev/dict/tree",
+
+            /* 文件下载 */
+            "/dev/file/download",
+
+            /* 用户个人中心相关 */
+            "/sys/userCenter/getPicCaptcha",
+            "/sys/userCenter/findPasswordGetPhoneValidCode",
+            "/sys/userCenter/findPasswordGetEmailValidCode",
+            "/sys/userCenter/findPasswordByPhone",
+            "/sys/userCenter/findPasswordByEmail"
+    };
+
+    /**
+     * 仅超管使用的接口地址集合
+     */
+    private static final String[] SUPER_PERMISSION_PATH_ARR = {
+            "/auth/session/**",
+            "/auth/third/page",
+            "/client/user/**",
+            "/sys/org/**",
+            "/sys/position/**",
+            "/sys/button/**",
+            "/sys/menu/**",
+            "/sys/module/**",
+            "/sys/role/**",
+            "/sys/user/**",
+            "/dev/config/**",
+            "/dev/dict/**",
+            "/dev/email/page",
+            "/dev/email/delete",
+            "/dev/email/detail",
+            "/dev/file/page",
+            "/dev/file/list",
+            "/dev/file/delete",
+            "/dev/file/detail",
+            "/dev/job/**",
+            "/dev/log/**",
+            "/dev/message/page",
+            "/dev/message/delete",
+            "/dev/message/detail",
+            "/dev/monitor/**",
+            "/dev/sms/page",
+            "/dev/sms/delete",
+            "/dev/sms/detail",
+            "/gen/basic/**",
+            "/gen/config/**",
+            "/mobile/menu/**",
+            "/mobile/module/**",
+    };
+
+    /**
+     * B端要排除的,相当于C端要认证的
+     */
+    private static final String[] CLIENT_USER_PERMISSION_PATH_ARR = {
+            "/auth/c/**",
+            "/client/c/**"
+    };
+
+    /**
+     * 注册跨域过滤器
+     */
+    @Bean
+    public SaServletFilter getSaServletFilter() {
+        return new SaServletFilter()
+                // 指定拦截路由
+                .addInclude("/**")
+
+                // 设置鉴权的接口
+                .setAuth(r -> {
+                    // B端的接口校验B端登录
+                    SaRouter.match("/**")
+                            // 排除无需登录接口
+                            .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
+                            // 排除C端认证接口
+                            .notMatch(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR))
+                            // 校验B端登录
+                            .check(r1 -> StpUtil.checkLogin());
+
+                    // C端的接口校验C端登录
+                    SaRouter.match("/**")
+                            // 排除无需登录接口
+                            .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
+                            // 匹配C端认证接口
+                            .match(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR))
+                            // 校验C端登录
+                            .check(r1 -> StpClientUtil.checkLogin());
+
+                    // B端的超管接口校验B端超管角色
+                    SaRouter.match("/**")
+                            // 排除无需登录接口
+                            .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
+                            // 匹配超管接口
+                            .match(CollectionUtil.newArrayList(SUPER_PERMISSION_PATH_ARR))
+                            // 校验B端超管角色
+                            .check(r1 -> StpUtil.checkRole(SysBuildInEnum.BUILD_IN_ROLE_CODE.getValue()));
+                })
+
+                // 前置函数:在每次认证函数之前执行
+                .setBeforeAuth(obj -> {
+
+                    // ---------- 设置跨域响应头 ----------
+                    SaHolder.getResponse()
+
+                            // 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
+                            // .setHeader("X-Frame-Options", "SAMEORIGIN")
+
+                            // 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
+                            .setHeader("X-XSS-Protection", "1; mode=block")
+                            // 禁用浏览器内容嗅探
+                            .setHeader("X-Content-Type-Options", "nosniff")
+                            // 允许指定域访问跨域资源
+                            .setHeader("Access-Control-Allow-Origin", "*")
+                            // 允许所有请求方式
+                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
+                            // 有效时间
+                            .setHeader("Access-Control-Max-Age", "3600")
+                            // 允许的header参数
+                            .setHeader("Access-Control-Allow-Headers", "*");
+
+                    // 如果是预检请求,则立即返回到前端
+                    SaRouter.match(SaHttpMethod.OPTIONS)
+                            // OPTIONS预检请求,不做处理
+                            .free(r -> {})
+                            .back();
+                })
+
+                // 异常处理
+                .setError(e -> {
+                    // 由于过滤器中抛出的异常不进入全局异常处理,所以必须提供[异常处理函数]来处理[认证函数]里抛出的异常
+                    // 在[异常处理函数]里的返回值,将作为字符串输出到前端,此处统一转为JSON输出前端
+                    SaResponse saResponse = SaHolder.getResponse();
+                    saResponse.setHeader(Header.CONTENT_TYPE.getValue(), ContentType.JSON + ";charset=" +  CharsetUtil.UTF_8);
+                    return GlobalExceptionUtil.getCommonResult((Exception) e);
+                });
+    }
+
+    /**
+     * RedisTemplate序列化
+     *
+     * @author xuyuxiang
+     * @date 2022/6/21 17:01
+     **/
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        redisTemplate.setKeySerializer(stringRedisSerializer);
+        redisTemplate.setHashKeySerializer(stringRedisSerializer);
+        Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    /**
+     * 静态资源映射
+     *
+     * @author xuyuxiang
+     * @date 2022/7/25 15:16
+     **/
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    /**
+     * 添加节流防抖拦截器
+     *
+     * @author xuyuxiang
+     * @date 2022/6/20 15:18
+     **/
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new HandlerInterceptor() {
+            @Override
+            public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
+                                     @NonNull Object handler) throws Exception {
+                if (handler instanceof HandlerMethod) {
+                    HandlerMethod handlerMethod = (HandlerMethod) handler;
+                    Method method = handlerMethod.getMethod();
+                    CommonNoRepeat annotation = method.getAnnotation(CommonNoRepeat.class);
+                    if (ObjectUtil.isNotEmpty(annotation)) {
+                        JSONObject repeatSubmitJsonObject = this.isRepeatSubmit(request, annotation);
+                        if (repeatSubmitJsonObject.getBool("repeat")) {
+                            response.setCharacterEncoding(CharsetUtil.UTF_8);
+                            response.setContentType(ContentType.JSON.toString());
+                            response.getWriter().write(JSONUtil.toJsonStr(CommonResult.error("请求过于频繁,请" + repeatSubmitJsonObject.getStr("time") + "后再试")));
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+
+            public JSONObject isRepeatSubmit(HttpServletRequest request, CommonNoRepeat annotation) {
+                JSONObject jsonObject = JSONUtil.createObj();
+                jsonObject.set("repeatParam", JSONUtil.toJsonStr(request.getParameterMap()));
+                jsonObject.set("repeatTime", DateUtil.current());
+                String url = request.getRequestURI();
+                // 获取该接口缓存的限流数据
+                Object cacheObj = commonCacheOperator.get(COMMON_REPEAT_SUBMIT_CACHE_KEY + url);
+                if (ObjectUtil.isNotEmpty(cacheObj)) {
+                    JSONObject cacheJsonObject = JSONUtil.parseObj(cacheObj);
+                    if(cacheJsonObject.containsKey(url)) {
+                        JSONObject existRepeatJsonObject = cacheJsonObject.getJSONObject(url);
+                        // 如果与上次参数一致,且时间间隔小于要求的限流时长,则判定为重复提交
+                        if (jsonObject.getStr("repeatParam").equals(existRepeatJsonObject.getStr("repeatParam"))) {
+                            long interval = jsonObject.getLong("repeatTime") - existRepeatJsonObject.getLong("repeatTime");
+                            if(interval < annotation.interval()) {
+                                long secondsParam = (annotation.interval() - interval) / 1000;
+                                if(secondsParam == 0) {
+                                    return JSONUtil.createObj().set("repeat", false);
+                                } else {
+                                    return JSONUtil.createObj().set("repeat", true).set("time", CommonTimeFormatUtil.formatSeconds(secondsParam));
+                                }
+                            }
+                        }
+                    }
+                }
+                // 缓存最新的该接口的限流数据,为防止缓存的数据过多,缓存时效为1小时
+                commonCacheOperator.put(COMMON_REPEAT_SUBMIT_CACHE_KEY + url, JSONUtil.createObj().set(url, jsonObject), 60 * 60);
+                return JSONUtil.createObj().set("repeat", false);
+            }
+        }).addPathPatterns("/**");
+    }
+
+    /**
+     * 通用Wrapper的AOP
+     *
+     * @author xuyuxiang
+     * @date 2022/9/15 21:24
+     */
+    @Component
+    @Aspect
+    public static class CommonWrapperAop {
+
+        /**
+         * 切入点
+         *
+         * @author xuyuxiang
+         * @date 2022/9/15 21:27
+         */
+        @Pointcut("@annotation(vip.xiaonuo.common.annotation.CommonWrapper)")
+        private void wrapperPointcut() {
+
+        }
+
+        /**
+         * 执行包装
+         *
+         * @author xuyuxiang
+         * @date 2022/9/15 21:27
+         */
+        @Around("wrapperPointcut()")
+        public Object doWrapper(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+            // 直接执行原有业务逻辑
+            Object proceedResult = proceedingJoinPoint.proceed();
+            return processWrapping(proceedingJoinPoint, proceedResult);
+        }
+
+        /**
+         * 具体包装过程
+         *
+         * @author xuyuxiang
+         * @date 2022/9/15 21:27
+         */
+        @SuppressWarnings("all")
+        private Object processWrapping(ProceedingJoinPoint proceedingJoinPoint, Object originResult) throws IllegalAccessException, InstantiationException {
+            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
+            Method method = methodSignature.getMethod();
+            CommonWrapper commonWrapper = method.getAnnotation(CommonWrapper.class);
+            Class<? extends CommonWrapperInterface<?>>[] baseWrapperClasses = commonWrapper.value();
+            if (ObjectUtil.isEmpty(baseWrapperClasses)) {
+                return originResult;
+            }
+            if (!(originResult instanceof CommonResult)) {
+                return originResult;
+            }
+            CommonResult commonResult = (CommonResult) originResult;
+            Object beWrapped = commonResult.getData();
+            if (ObjectUtil.isBasicType(beWrapped)) {
+                throw new CommonException("被包装的值不能是基本类型");
+            }
+            if (beWrapped instanceof Page) {
+                Page page = (Page) beWrapped;
+                ArrayList<Map<String, Object>> maps = new ArrayList<>();
+                for (Object wrappedItem : page.getRecords()) {
+                    maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
+                }
+                page.setRecords(maps);
+                commonResult.setData(page);
+            } else if (beWrapped instanceof Collection) {
+                Collection collection = (Collection) beWrapped;
+                List<Map<String, Object>> maps = new ArrayList<>();
+                for (Object wrappedItem : collection) {
+                    maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
+                }
+                commonResult.setData(maps);
+            } else if (ArrayUtil.isArray(beWrapped)) {
+                Object[] objects = this.objToArray(beWrapped);
+                ArrayList<Map<String, Object>> maps = new ArrayList<>();
+                for (Object wrappedItem : objects) {
+                    maps.add(this.wrapPureObject(wrappedItem, baseWrapperClasses));
+                }
+                commonResult.setData(maps);
+            } else {
+                commonResult.setData(this.wrapPureObject(beWrapped, baseWrapperClasses));
+            }
+            return commonResult;
+        }
+
+        /**
+         * 原始对象包装JSONObject
+         *
+         * @author xuyuxiang
+         * @date 2022/9/15 21:36
+         */
+        @SuppressWarnings("all")
+        private JSONObject wrapPureObject(Object originModel, Class<? extends CommonWrapperInterface<?>>[] baseWrapperClasses) {
+            JSONObject jsonObject = JSONUtil.parseObj(originModel);
+            try {
+                for (Class<? extends CommonWrapperInterface<?>> commonWrapperClass : baseWrapperClasses) {
+                    CommonWrapperInterface commonWrapperInterface = commonWrapperClass.newInstance();
+                    Map<String, Object> incrementFieldsMap = commonWrapperInterface.doWrap(originModel);
+                    jsonObject.putAll(incrementFieldsMap);
+                }
+            } catch (Exception e) {
+                throw new CommonException("原始对象包装过程,字段转化异常:{}", e.getMessage());
+            }
+            return jsonObject;
+        }
+
+        /**
+         * Object转array
+         *
+         * @author xuyuxiang
+         * @date 2022/9/15 21:34
+         */
+        private Object[] objToArray(Object object) {
+            int length = Array.getLength(object);
+            Object[] result = new Object[length];
+            for (int i = 0; i < result.length; i++) {
+                result[i] = Array.get(object, i);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * 分页插件
+     *
+     * @author xuyuxiang
+     * @date 2022/3/11 10:59
+     **/
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
+        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+        return mybatisPlusInterceptor;
+    }
+
+    /**
+     * 数据库id选择器,用于Mapper.xml中
+     * MyBatis可以根据不同的数据库厂商执行不同的语句
+     *
+     * @author xuyuxiang
+     * @date 2022/1/8 2:16
+     */
+    @Component
+    public static class CustomDbIdProvider implements DatabaseIdProvider {
+
+        @Override
+        public String getDatabaseId(DataSource dataSource) throws SQLException {
+            Connection conn = null;
+            try {
+                conn = dataSource.getConnection();
+                String url = conn.getMetaData().getURL().toLowerCase();
+                if (url.contains("jdbc:oracle")) {
+                    return "oracle";
+                } else if (url.contains("jdbc:postgresql")) {
+                    return "pgsql";
+                } else if (url.contains("jdbc:mysql")) {
+                    return "mysql";
+                } else if (url.contains("jdbc:dm")) {
+                    return "dm";
+                } else if (url.contains("jdbc:kingbase")) {
+                    return "kingbase";
+                }  else {
+                    return "mysql";
+                }
+            } finally {
+                JdbcUtils.closeConnection(conn);
+            }
+        }
+    }
+
+    /**
+     * 自定义公共字段自动注入
+     *
+     * @author xuyuxiang
+     * @date 2020/3/31 15:42
+     */
+    @Component
+    public static class CustomMetaObjectHandler implements MetaObjectHandler {
+        /** 删除标志 */
+        private static final String DELETE_FLAG = "deleteFlag";
+
+        /** 创建人 */
+        private static final String CREATE_USER = "createUser";
+
+        /** 创建时间 */
+        private static final String CREATE_TIME = "createTime";
+
+        /** 更新人 */
+        private static final String UPDATE_USER = "updateUser";
+
+        /** 更新时间 */
+        private static final String UPDATE_TIME = "updateTime";
+
+        @Override
+        public void insertFill(MetaObject metaObject) {
+            try {
+                //为空则设置deleteFlag
+                Object deleteFlag = metaObject.getValue(DELETE_FLAG);
+                if (ObjectUtil.isNull(deleteFlag)) {
+                    setFieldValByName(DELETE_FLAG, EnumUtil.toString(CommonDeleteFlagEnum.NOT_DELETE), metaObject);
+                }
+            } catch (ReflectionException ignored) { }
+            try {
+                //为空则设置createUser
+                Object createUser = metaObject.getValue(CREATE_USER);
+                if (ObjectUtil.isNull(createUser)) {
+                    setFieldValByName(CREATE_USER, this.getUserId(), metaObject);
+                }
+            } catch (ReflectionException ignored) { }
+            try {
+                //为空则设置createTime
+                Object createTime = metaObject.getValue(CREATE_TIME);
+                if (ObjectUtil.isNull(createTime)) {
+                    setFieldValByName(CREATE_TIME, DateTime.now(), metaObject);
+                }
+            } catch (ReflectionException ignored) { }
+        }
+
+        @Override
+        public void updateFill(MetaObject metaObject) {
+            try {
+                //设置updateUser
+                setFieldValByName(UPDATE_USER, this.getUserId(), metaObject);
+            } catch (ReflectionException ignored) { }
+            try {
+                //设置updateTime
+                setFieldValByName(UPDATE_TIME, DateTime.now(), metaObject);
+            } catch (ReflectionException ignored) { }
+        }
+
+        /**
+         * 获取用户id
+         */
+        private String getUserId() {
+            try {
+                String loginId = StpUtil.getLoginIdAsString();
+                if (ObjectUtil.isNotEmpty(loginId)) {
+                    return loginId;
+                } else {
+                    return "-1";
+                }
+            } catch (Exception e) {
+                return "-1";
+            }
+
+        }
+
+        /**
+         * 获取用户组织id
+         */
+        private String getUserOrgId() {
+            try {
+                String orgId = StpLoginUserUtil.getLoginUser().getOrgId();
+                if (ObjectUtil.isNotEmpty(orgId)) {
+
+                    return orgId;
+                } else {
+                    return "-1";
+                }
+            } catch (Exception e) {
+                return "-1";
+            }
+
+        }
+    }
+
+    /**
+     * 注册数据变化事件中心 事件发布器
+     *
+     * @author xuyuxiang
+     * @date 2023/3/3 14:27
+     **/
+    @Resource
+    public void registerListenerList(List<CommonDataChangeListener> dataChangeListenerList) {
+        CommonDataChangeEventCenter.registerListenerList(dataChangeListenerList);
+    }
+}

+ 20 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/converter/MultipartJackson2HttpMessageConverter.java

@@ -0,0 +1,20 @@
+//package vip.xiaonuo.core.converter;
+//
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import org.springframework.http.MediaType;
+//import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
+//import org.springframework.stereotype.Component;
+//
+//import javax.sound.sampled.BooleanControl;
+//
+//@Component
+//public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
+//    protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
+//        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
+//    }
+//
+//    @Override
+//    public boolean canWrite(Class<?> clazz, MediaType mediaType){
+//        return false;
+//    }
+//}

+ 37 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/enums/BaseDataTypeEnum.java

@@ -0,0 +1,37 @@
+package vip.xiaonuo.core.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum BaseDataTypeEnum {
+    /**
+     * 栋舍
+     */
+    PIGPEN("PIGPEN"),
+
+    /**
+     * 基础配置
+     */
+    BASE_CONFIG("BASE_CONFIG"),
+
+    /**
+     * 物资领用
+     */
+    INVENTORY_USE("INVENTORY_USE"),
+
+    /**
+     * 部门
+     */
+    DEPARTMENT("DEPARTMENT"),
+
+    /**
+     * 季节
+     */
+    SEASON("SEASON");
+
+    private final String value;
+
+    BaseDataTypeEnum(String value) {
+        this.value = value;
+    }
+}

+ 18 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/enums/EnvDataTypeEnum.java

@@ -0,0 +1,18 @@
+package vip.xiaonuo.core.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum EnvDataTypeEnum {
+
+    /**
+     * 设备
+     */
+    DEVICE("DEVICE");
+
+    private final String value;
+
+    EnvDataTypeEnum(String value){
+        this.value = value;
+    }
+}

+ 75 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorAttributesHandler.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.handler;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.HttpStatus;
+import org.springframework.boot.web.error.ErrorAttributeOptions;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.WebRequest;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.util.CommonServletUtil;
+
+import java.util.Map;
+
+/**
+ * 将未知错误异常,输出格式重写为我们熟悉的响应格式
+ *
+ * @author xuyuxiang
+ * @date 2021/10/9 15:24
+ **/
+@Component
+public class GlobalErrorAttributesHandler extends DefaultErrorAttributes {
+    @Override
+    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions attributeOptions) {
+        // 获取spring默认的返回内容
+        Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(webRequest, attributeOptions);
+
+        // 获取其状态码
+        Object status = defaultErrorAttributes.get("status");
+        if (ObjectUtil.isNotEmpty(status)) {
+            // 如果其为404,则处理
+            if (HttpStatus.HTTP_NOT_FOUND == Convert.toInt(status)) {
+                Object path = defaultErrorAttributes.get("path");
+                if(ObjectUtil.isNotEmpty(path)) {
+                    return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在,请求地址:" +
+                            Convert.toStr(path), null));
+                } else {
+                    return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在", null));
+                }
+            } else {
+                return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, "服务器异常,请求地址:" +
+                        CommonServletUtil.getRequest().getRequestURL(), null));
+            }
+        }
+
+        // 如果返回的异常是CommonException,则按CommonException响应的内容进行返回
+        Throwable throwable = this.getError(webRequest);
+        if (ObjectUtil.isNotEmpty(throwable)) {
+            if (throwable instanceof CommonException) {
+                CommonException commonException = (CommonException) throwable;
+                return BeanUtil.beanToMap(CommonResult.error(commonException.getMsg()));
+            } else {
+                return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, "服务器异常,请求地址:" +
+                        CommonServletUtil.getRequest().getRequestURL(), null));
+            }
+        } else {
+            // throwable为空,则直接返回默认异常
+            return BeanUtil.beanToMap(CommonResult.error());
+        }
+    }
+}

+ 67 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorViewController.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.handler;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.pojo.CommonResult;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 全局异常页面处理器,覆盖默认的Whitelabel Error Page
+ *
+ * @author xuyuxiang
+ * @date 2022/2/11 15:41
+ **/
+@Slf4j
+@RestController
+public class GlobalErrorViewController {
+
+    /**
+     * Error页面视图,直接响应JSON
+     *
+     * @author xuyuxiang
+     * @date 2022/2/11 16:11
+     **/
+    @RequestMapping("/errorView")
+    public CommonResult<String> globalError(HttpServletRequest request) {
+        CommonResult<String> commonResult = new CommonResult<>(404, "路径不存在", null);
+        Object model = request.getAttribute("model");
+        if(ObjectUtil.isNotEmpty(model)) {
+            if(model instanceof Exception){
+                if(model instanceof CommonException) {
+                    JSONObject errorObj = JSONUtil.parseObj(model);
+                    Integer code = errorObj.getInt("code");
+                    String msg = errorObj.getStr("msg");
+                    if(ObjectUtil.isAllNotEmpty(code, msg)) {
+                        commonResult.setCode(code).setMsg(msg);
+                    } else if(ObjectUtil.isNotEmpty(msg)) {
+                        commonResult = CommonResult.error(msg);
+                    } else {
+                        commonResult = CommonResult.error();
+                    }
+                } else {
+                    commonResult = CommonResult.error();
+                    log.error(">>> 服务器未知异常,具体信息:", (Exception) model);
+                }
+            }
+        }
+        return commonResult;
+    }
+}

+ 73 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalErrorViewHandler.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.handler;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * 全局异常页面处理器,覆盖默认的Whitelabel Error Page
+ *
+ * @author xuyuxiang
+ * @date 2022/2/11 15:41
+ **/
+@RestController
+public class GlobalErrorViewHandler extends BasicErrorController {
+
+    public GlobalErrorViewHandler(ServerProperties serverProperties) {
+        super(new GlobalErrorAttributesHandler(), serverProperties.getError());
+    }
+
+    /**
+     * 覆盖默认的Json响应
+     *
+     * @author xuyuxiang
+     * @date 2022/2/11 15:47
+     **/
+    @Override
+    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
+        Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
+        Integer code = Convert.toInt(defaultErrorAttributes.get("code"));
+        return new ResponseEntity<>(defaultErrorAttributes, HttpStatus.valueOf(ObjectUtil.isNotEmpty(code)?code:500));
+    }
+
+    /**
+     * 覆盖默认的错误页面,响应JSON
+     *
+     * @author xuyuxiang
+     * @date 2022/2/12 21:55
+     */
+    @Override
+    @RequestMapping(produces = {"text/html"})
+    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
+        HttpStatus status = this.getStatus(request);
+        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
+        response.setStatus(status.value());
+        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
+        request.setAttribute("model", model);
+        return modelAndView != null ? modelAndView : new ModelAndView("errorView", model);
+    }
+}

+ 40 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalExceptionHandler.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.handler;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import vip.xiaonuo.common.pojo.CommonResult;
+
+/**
+ * 全局异常处理器
+ *
+ * @author xuyuxiang
+ * @date 2021/10/9 14:59
+ **/
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    /**
+     * 不同异常返回不同结果
+     *
+     * @author xuyuxiang
+     * @date 2022/7/28 16:54
+     **/
+    @ResponseBody
+    @ExceptionHandler
+    public CommonResult<String> handleException(Exception e) {
+        return GlobalExceptionUtil.getCommonResult(e);
+    }
+}

+ 190 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/handler/GlobalExceptionUtil.java

@@ -0,0 +1,190 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.core.handler;
+
+import cn.dev33.satoken.exception.SaTokenException;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpStatus;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.exceptions.PersistenceException;
+import org.mybatis.spring.MyBatisSystemException;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.multipart.MultipartException;
+import org.springframework.web.multipart.support.MissingServletRequestPartException;
+import vip.xiaonuo.auth.core.util.AuthExceptionUtil;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.util.CommonServletUtil;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 全局异常处理工具类,将异常转为通用结果
+ *
+ * @author xuyuxiang
+ * @date 2021/12/18 16:44
+ */
+@Slf4j
+public class GlobalExceptionUtil {
+
+    /**
+     * 根据错误类型获取对应的CommonResult
+     *
+     * @author xuyuxiang
+     * @date 2021/10/11 15:52
+     **/
+    public static CommonResult<String> getCommonResult(Exception e) {
+        CommonResult<String> commonResult;
+        if (e instanceof HttpRequestMethodNotSupportedException) {
+
+            // 如果是请求方法异常 405
+            String method = CommonServletUtil.getRequest().getMethod();
+            if (HttpMethod.GET.toString().equals(method)) {
+                commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为POST", null);
+            } else if(HttpMethod.POST.toString().equals(method)) {
+                commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为GET", null);
+            } else {
+                commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法仅支持GET或POST", null);
+            }
+        } else if (e instanceof HttpMessageNotReadableException) {
+            log.error(">>> 参数传递格式异常:", e);
+            // 如果是参数传递格式不支持异常 415
+            if (e.getMessage().contains("JSON parse error")) {
+                //JSON格式转换错误特殊提示
+                commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
+            } else {
+                commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "请使用JSON方式传参", null);
+            }
+        } else if (e instanceof HttpMediaTypeNotSupportedException) {
+            log.error(">>> 参数传递格式异常:", e);
+            // 如果是JSON参数格式错误异常 415
+            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
+        } else if (e instanceof MethodArgumentNotValidException) {
+
+            // 如果是参数校验异常(MethodArgumentNotValidException) 415
+            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
+            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(methodArgumentNotValidException.getBindingResult()), null);
+        } else if (e instanceof BindException) {
+
+            // 如果是参数校验异常(BindException) 415
+            BindException bindException = (BindException) e;
+            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(bindException.getBindingResult()), null);
+        } else if (e instanceof ConstraintViolationException) {
+
+            // 如果是参数校验异常(ConstraintViolationException) 415
+            ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
+            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(constraintViolationException.getConstraintViolations()), null);
+        } else if (e instanceof MissingServletRequestParameterException) {
+
+            // 如果是参数校验异常(MissingServletRequestParameterException) 415
+            MissingServletRequestParameterException missingServletRequestParameterException = (MissingServletRequestParameterException) e;
+            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, missingServletRequestParameterException.getMessage(), null);
+        }
+        else if (e instanceof MultipartException) {
+            log.error(">>> 文件上传参数异常:", e);
+            //文件上传错误特殊提示
+            commonResult = CommonResult.error("请使用multipart/form-data方式上传文件");
+        } else if (e instanceof MissingServletRequestPartException) {
+            log.error(">>> 文件上传参数异常:", e);
+            //文件上传错误特殊提示
+            commonResult = CommonResult.error("请选择要上传的文件并检查文件参数名称是否正确");
+        } else if (e instanceof SaTokenException) {
+
+            // 如果是SaToken相关异常,则由AuthExceptionUtil处理
+            return AuthExceptionUtil.getCommonResult(e);
+        } else if(e instanceof MyBatisSystemException) {
+
+            // 如果是MyBatisSystemException
+            Throwable cause = e.getCause();
+            if (cause instanceof PersistenceException) {
+                Throwable secondCause = cause.getCause();
+                if (secondCause instanceof CommonException) {
+                    CommonException commonException = (CommonException) secondCause;
+                    commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
+                } else {
+                    log.error(">>> 数据操作异常:", e);
+                    commonResult = CommonResult.error("数据操作异常");
+                }
+            } else {
+                log.error(">>> 数据操作异常:", e);
+                commonResult = CommonResult.error("数据操作异常");
+            }
+        } else if (e instanceof CommonException) {
+
+            // 通用业务异常,直接返回给前端
+            CommonException commonException = (CommonException) e;
+            commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
+        }  else {
+            // 未知异常打印详情
+            log.error(">>> 服务器未知异常,请求地址:{},具体信息:", CommonServletUtil.getRequest().getRequestURL(), e);
+            // 未知异常返回服务器异常
+            commonResult = CommonResult.error();
+        }
+        return commonResult;
+    }
+
+    /**
+     * 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
+     *
+     * @author xuyuxiang
+     * @date 2021/10/12 11:14
+     **/
+    public static String getArgNotValidMessage(Set<ConstraintViolation<?>> constraintViolationSet) {
+        if (ObjectUtil.isEmpty(constraintViolationSet)) {
+            return "";
+        }
+        StringBuilder stringBuilder = StrUtil.builder();
+
+        // 多个错误用逗号分隔
+        for (ConstraintViolation<?> constraintViolation : constraintViolationSet) {
+            stringBuilder.append(StrUtil.COMMA).append(constraintViolation.getMessage());
+        }
+
+        // 最终把首部的逗号去掉
+        return StrUtil.removePrefix(stringBuilder.toString(), StrUtil.COMMA);
+    }
+
+    /**
+     * 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
+     *
+     * @author xuyuxiang
+     * @date 2021/10/12 11:14
+     **/
+    public static String getArgNotValidMessage(BindingResult bindingResult) {
+        if (ObjectUtil.isNull(bindingResult)) {
+            return "";
+        }
+        StringBuilder stringBuilder = StrUtil.builder();
+
+        // 多个错误用逗号分隔
+        List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
+        for (ObjectError error : allErrorInfos) {
+            stringBuilder.append(StrUtil.COMMA).append(error.getDefaultMessage());
+        }
+
+        // 最终把首部的逗号去掉
+        return StrUtil.removePrefix(stringBuilder.toString(), StrUtil.COMMA);
+    }
+}

+ 14 - 0
huimv-farm/src/main/java/vip/xiaonuo/core/param/OrgIdParam.java

@@ -0,0 +1,14 @@
+package vip.xiaonuo.core.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class OrgIdParam {
+    /** 组织id */
+    @ApiModelProperty(value = "组织id", required = true,position = 99)
+    @NotBlank(message = "orgId不能为空")
+    private String orgId;
+}

+ 67 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/config/HrConfigure.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.config;
+
+
+import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import vip.xiaonuo.common.pojo.CommonResult;
+
+import javax.annotation.Resource;
+
+/**
+ * SaToken鉴权配置
+ *
+ * @HRor xuyuxiang
+ * @date 2021/10/9 14:24
+ **/
+@Configuration
+public class HrConfigure implements WebMvcConfigurer {
+
+    @Resource
+    private OpenApiExtensionResolver openApiExtensionResolver;
+
+    
+
+
+    @Bean(value = "HRDocApi")
+    public Docket hrDocApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(new ApiInfoBuilder()
+                        .title("人员模块HR")
+                        .description("人员模块HR")
+                        .termsOfServiceUrl("https://www.baiduc.com")
+                        .contact(new Contact("SNOWY_TEAM","https://www.baiduc.com", "xuyuxiang29@foxmail.com"))
+                        .version("2.0.0")
+                        .build())
+                .globalResponseMessage(RequestMethod.GET, CommonResult.responseList())
+                .globalResponseMessage(RequestMethod.POST, CommonResult.responseList())
+                .groupName("人员模块HR")
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .apis(RequestHandlerSelectors.basePackage("vip.xiaonuo.hr"))
+                .paths(PathSelectors.any())
+                .build().extensions(openApiExtensionResolver.buildExtensions("人员模块HR"));
+    }
+}

+ 131 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/controller/HrBaseEmployeeInfoController.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.entity.HrBaseEmployeeInfo;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoAddParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoEditParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoIdParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoPageParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.service.HrBaseEmployeeInfoService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 人员明细控制器
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ */
+@Api(tags = "人员明细控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class HrBaseEmployeeInfoController {
+
+    @Resource
+    private HrBaseEmployeeInfoService hrBaseEmployeeInfoService;
+
+    /**
+     * 获取人员明细分页
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取人员明细分页")
+    @SaCheckPermission("/hr/baseemployeeinfo/page")
+    @GetMapping("/hr/baseemployeeinfo/page")
+    public CommonResult<Page<HrBaseEmployeeInfo>> page(HrBaseEmployeeInfoPageParam hrBaseEmployeeInfoPageParam) {
+        return CommonResult.data(hrBaseEmployeeInfoService.page(hrBaseEmployeeInfoPageParam));
+    }
+
+    /**
+     * 添加人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加人员明细")
+    @CommonLog("添加人员明细")
+    @SaCheckPermission("/hr/baseemployeeinfo/add")
+    @PostMapping("/hr/baseemployeeinfo/add")
+    public CommonResult<String> add(@RequestBody @Valid HrBaseEmployeeInfoAddParam hrBaseEmployeeInfoAddParam) {
+        hrBaseEmployeeInfoService.add(hrBaseEmployeeInfoAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑人员明细")
+    @CommonLog("编辑人员明细")
+    @SaCheckPermission("/hr/baseemployeeinfo/edit")
+    @PostMapping("/hr/baseemployeeinfo/edit")
+    public CommonResult<String> edit(@RequestBody @Valid HrBaseEmployeeInfoEditParam hrBaseEmployeeInfoEditParam) {
+        hrBaseEmployeeInfoService.edit(hrBaseEmployeeInfoEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除人员明细")
+    @CommonLog("删除人员明细")
+    @SaCheckPermission("/hr/baseemployeeinfo/delete")
+    @PostMapping("/hr/baseemployeeinfo/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<HrBaseEmployeeInfoIdParam> hrBaseEmployeeInfoIdParamList) {
+        hrBaseEmployeeInfoService.delete(hrBaseEmployeeInfoIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取人员明细详情
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取人员明细详情")
+    @SaCheckPermission("/hr/baseemployeeinfo/detail")
+    @GetMapping("/hr/baseemployeeinfo/detail")
+    public CommonResult<HrBaseEmployeeInfo> detail(@Valid HrBaseEmployeeInfoIdParam hrBaseEmployeeInfoIdParam) {
+        return CommonResult.data(hrBaseEmployeeInfoService.detail(hrBaseEmployeeInfoIdParam));
+    }
+}

+ 159 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/entity/HrBaseEmployeeInfo.java

@@ -0,0 +1,159 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 人员明细实体
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+@Setter
+@TableName("hr_base_employee_info")
+public class HrBaseEmployeeInfo {
+
+    /** ID */
+    @TableId
+    @ApiModelProperty(value = "ID", position = 1)
+    private String id;
+
+    /** 头像 */
+    @ApiModelProperty(value = "头像", position = 2)
+    private String avatar;
+
+    /** 姓名 */
+    @ApiModelProperty(value = "姓名", position = 3)
+    private String name;
+
+    /** 性别 */
+    @ApiModelProperty(value = "性别", position = 4)
+    private String gender;
+
+    /** 年龄 */
+    @ApiModelProperty(value = "年龄", position = 5)
+    private String age;
+
+    /** 出生日期 */
+    @ApiModelProperty(value = "出生日期", position = 6)
+    private String birthday;
+
+    /** 民族 */
+    @ApiModelProperty(value = "民族", position = 7)
+    private String nation;
+
+    /** 籍贯 */
+    @ApiModelProperty(value = "籍贯", position = 8)
+    private String nativePlace;
+
+    /** 家庭住址 */
+    @ApiModelProperty(value = "家庭住址", position = 9)
+    private String homeAddress;
+
+    /** 通信地址 */
+    @ApiModelProperty(value = "通信地址", position = 10)
+    private String mailingAddress;
+
+    /** 证件类型 */
+    @ApiModelProperty(value = "证件类型", position = 11)
+    private String idCardType;
+
+    /** 证件号码 */
+    @ApiModelProperty(value = "证件号码", position = 12)
+    private String idCardNumber;
+
+    /** 文化程度 */
+    @ApiModelProperty(value = "文化程度", position = 13)
+    private String cultureLevel;
+
+    /** 政治面貌 */
+    @ApiModelProperty(value = "政治面貌", position = 14)
+    private String politicalOutlook;
+
+    /** 学历 */
+    @ApiModelProperty(value = "学历", position = 15)
+    private String education;
+
+    /** 手机 */
+    @ApiModelProperty(value = "手机", position = 16)
+    private String phone;
+
+    /** 入职日期 */
+    @ApiModelProperty(value = "入职日期", position = 17)
+    private String entryDate;
+
+    /** 机构id */
+    @ApiModelProperty(value = "机构id", position = 18)
+    private String departmentId;
+
+    /** 删除标志 */
+    @ApiModelProperty(value = "删除标志", position = 19)
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** 创建时间 */
+    @ApiModelProperty(value = "创建时间", position = 20)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 创建用户 */
+    @ApiModelProperty(value = "创建用户", position = 21)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** 修改时间 */
+    @ApiModelProperty(value = "修改时间", position = 22)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** 修改用户 */
+    @ApiModelProperty(value = "修改用户", position = 23)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+
+    /** 司龄 */
+    @ApiModelProperty(value = "司龄", position = 24)
+    private Integer siLing;
+
+    /** 薪酬 */
+    @ApiModelProperty(value = "薪酬", position = 25)
+    private Integer pay;
+
+    /** 产值 */
+    @ApiModelProperty(value = "产值", position = 26)
+    private Integer outputValue;
+
+    /** 0在职1离职2退休返聘 */
+    @ApiModelProperty(value = "0在职1离职2退休返聘", position = 27)
+    private Integer userType;
+
+    /** 预计退休时间 */
+    @ApiModelProperty(value = "预计退休时间", position = 28)
+    private Date retireTime;
+
+    /** 0 未婚 1已婚 */
+    @ApiModelProperty(value = "0 未婚 1已婚", position = 29)
+    private Integer marriage;
+
+    /** 工龄 */
+    @ApiModelProperty(value = "工龄", position = 30)
+    private Integer seniority;
+}

+ 34 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/enums/HrBaseEmployeeInfoEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.enums;
+
+import lombok.Getter;
+
+/**
+ * 人员明细枚举
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+public enum HrBaseEmployeeInfoEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    HrBaseEmployeeInfoEnum(String value) {
+        this.value = value;
+    }
+}

+ 25 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/mapper/HrBaseEmployeeInfoMapper.java

@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.entity.HrBaseEmployeeInfo;
+
+/**
+ * 人员明细Mapper接口
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+public interface HrBaseEmployeeInfoMapper extends BaseMapper<HrBaseEmployeeInfo> {
+}

+ 5 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/mapper/mapping/HrBaseEmployeeInfoMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="vip.xiaonuo.hr.modular.baseemployeeinfo.mapper.HrBaseEmployeeInfoMapper">
+
+</mapper>

+ 127 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoAddParam.java

@@ -0,0 +1,127 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 人员明细添加参数
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+@Setter
+public class HrBaseEmployeeInfoAddParam {
+
+    /** 头像 */
+    @ApiModelProperty(value = "头像", position = 2)
+    private String avatar;
+
+    /** 姓名 */
+    @ApiModelProperty(value = "姓名", position = 3)
+    private String name;
+
+    /** 性别 */
+    @ApiModelProperty(value = "性别", position = 4)
+    private String gender;
+
+    /** 年龄 */
+    @ApiModelProperty(value = "年龄", position = 5)
+    private String age;
+
+    /** 出生日期 */
+    @ApiModelProperty(value = "出生日期", position = 6)
+    private String birthday;
+
+    /** 民族 */
+    @ApiModelProperty(value = "民族", position = 7)
+    private String nation;
+
+    /** 籍贯 */
+    @ApiModelProperty(value = "籍贯", position = 8)
+    private String nativePlace;
+
+    /** 家庭住址 */
+    @ApiModelProperty(value = "家庭住址", position = 9)
+    private String homeAddress;
+
+    /** 通信地址 */
+    @ApiModelProperty(value = "通信地址", position = 10)
+    private String mailingAddress;
+
+    /** 证件类型 */
+    @ApiModelProperty(value = "证件类型", position = 11)
+    private String idCardType;
+
+    /** 证件号码 */
+    @ApiModelProperty(value = "证件号码", position = 12)
+    private String idCardNumber;
+
+    /** 文化程度 */
+    @ApiModelProperty(value = "文化程度", position = 13)
+    private String cultureLevel;
+
+    /** 政治面貌 */
+    @ApiModelProperty(value = "政治面貌", position = 14)
+    private String politicalOutlook;
+
+    /** 学历 */
+    @ApiModelProperty(value = "学历", position = 15)
+    private String education;
+
+    /** 手机 */
+    @ApiModelProperty(value = "手机", position = 16)
+    private String phone;
+
+    /** 入职日期 */
+    @ApiModelProperty(value = "入职日期", position = 17)
+    private String entryDate;
+
+    /** 机构id */
+    @ApiModelProperty(value = "机构id", position = 18)
+    private String departmentId;
+
+    /** 司龄 */
+    @ApiModelProperty(value = "司龄", position = 24)
+    private Integer siLing;
+
+    /** 薪酬 */
+    @ApiModelProperty(value = "薪酬", position = 25)
+    private Integer pay;
+
+    /** 产值 */
+    @ApiModelProperty(value = "产值", position = 26)
+    private Integer outputValue;
+
+    /** 0在职1离职2退休返聘 */
+    @ApiModelProperty(value = "0在职1离职2退休返聘", position = 27)
+    private Integer userType;
+
+    /** 预计退休时间 */
+    @ApiModelProperty(value = "预计退休时间", position = 28)
+    private Date retireTime;
+
+    /** 0 未婚 1已婚 */
+    @ApiModelProperty(value = "0 未婚 1已婚", position = 29)
+    private Integer marriage;
+
+    /** 工龄 */
+    @ApiModelProperty(value = "工龄", position = 30)
+    private Integer seniority;
+
+}

+ 133 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoEditParam.java

@@ -0,0 +1,133 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import java.util.Date;
+
+/**
+ * 人员明细编辑参数
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+@Setter
+public class HrBaseEmployeeInfoEditParam {
+
+    /** ID */
+    @ApiModelProperty(value = "ID", required = true, position = 1)
+    @NotBlank(message = "id不能为空")
+    private String id;
+
+    /** 头像 */
+    @ApiModelProperty(value = "头像", position = 2)
+    private String avatar;
+
+    /** 姓名 */
+    @ApiModelProperty(value = "姓名", position = 3)
+    private String name;
+
+    /** 性别 */
+    @ApiModelProperty(value = "性别", position = 4)
+    private String gender;
+
+    /** 年龄 */
+    @ApiModelProperty(value = "年龄", position = 5)
+    private String age;
+
+    /** 出生日期 */
+    @ApiModelProperty(value = "出生日期", position = 6)
+    private String birthday;
+
+    /** 民族 */
+    @ApiModelProperty(value = "民族", position = 7)
+    private String nation;
+
+    /** 籍贯 */
+    @ApiModelProperty(value = "籍贯", position = 8)
+    private String nativePlace;
+
+    /** 家庭住址 */
+    @ApiModelProperty(value = "家庭住址", position = 9)
+    private String homeAddress;
+
+    /** 通信地址 */
+    @ApiModelProperty(value = "通信地址", position = 10)
+    private String mailingAddress;
+
+    /** 证件类型 */
+    @ApiModelProperty(value = "证件类型", position = 11)
+    private String idCardType;
+
+    /** 证件号码 */
+    @ApiModelProperty(value = "证件号码", position = 12)
+    private String idCardNumber;
+
+    /** 文化程度 */
+    @ApiModelProperty(value = "文化程度", position = 13)
+    private String cultureLevel;
+
+    /** 政治面貌 */
+    @ApiModelProperty(value = "政治面貌", position = 14)
+    private String politicalOutlook;
+
+    /** 学历 */
+    @ApiModelProperty(value = "学历", position = 15)
+    private String education;
+
+    /** 手机 */
+    @ApiModelProperty(value = "手机", position = 16)
+    private String phone;
+
+    /** 入职日期 */
+    @ApiModelProperty(value = "入职日期", position = 17)
+    private String entryDate;
+
+    /** 机构id */
+    @ApiModelProperty(value = "机构id", position = 18)
+    private String departmentId;
+
+    /** 司龄 */
+    @ApiModelProperty(value = "司龄", position = 24)
+    private Integer siLing;
+
+    /** 薪酬 */
+    @ApiModelProperty(value = "薪酬", position = 25)
+    private Integer pay;
+
+    /** 产值 */
+    @ApiModelProperty(value = "产值", position = 26)
+    private Integer outputValue;
+
+    /** 0在职1离职2退休返聘 */
+    @ApiModelProperty(value = "0在职1离职2退休返聘", position = 27)
+    private Integer userType;
+
+    /** 预计退休时间 */
+    @ApiModelProperty(value = "预计退休时间", position = 28)
+    private Date retireTime;
+
+    /** 0 未婚 1已婚 */
+    @ApiModelProperty(value = "0 未婚 1已婚", position = 29)
+    private Integer marriage;
+
+    /** 工龄 */
+    @ApiModelProperty(value = "工龄", position = 30)
+    private Integer seniority;
+
+}

+ 35 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoIdParam.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 人员明细Id参数
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+@Setter
+public class HrBaseEmployeeInfoIdParam {
+
+    /** ID */
+    @ApiModelProperty(value = "ID", required = true)
+    @NotBlank(message = "id不能为空")
+    private String id;
+}

+ 49 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/param/HrBaseEmployeeInfoPageParam.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 人员明细查询参数
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Getter
+@Setter
+public class HrBaseEmployeeInfoPageParam {
+
+    /** 当前页 */
+    @ApiModelProperty(value = "当前页码")
+    private Integer current;
+
+    /** 每页条数 */
+    @ApiModelProperty(value = "每页条数")
+    private Integer size;
+
+    /** 排序字段 */
+    @ApiModelProperty(value = "排序字段,字段驼峰名称,如:userName")
+    private String sortField;
+
+    /** 排序方式 */
+    @ApiModelProperty(value = "排序方式,升序:ASCEND;降序:DESCEND")
+    private String sortOrder;
+
+    /** 关键词 */
+    @ApiModelProperty(value = "关键词")
+    private String searchKey;
+
+}

+ 80 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/service/HrBaseEmployeeInfoService.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.entity.HrBaseEmployeeInfo;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoAddParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoEditParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoIdParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoPageParam;
+
+import java.util.List;
+
+/**
+ * 人员明细Service接口
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+public interface HrBaseEmployeeInfoService extends IService<HrBaseEmployeeInfo> {
+
+    /**
+     * 获取人员明细分页
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    Page<HrBaseEmployeeInfo> page(HrBaseEmployeeInfoPageParam hrBaseEmployeeInfoPageParam);
+
+    /**
+     * 添加人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    void add(HrBaseEmployeeInfoAddParam hrBaseEmployeeInfoAddParam);
+
+    /**
+     * 编辑人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    void edit(HrBaseEmployeeInfoEditParam hrBaseEmployeeInfoEditParam);
+
+    /**
+     * 删除人员明细
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    void delete(List<HrBaseEmployeeInfoIdParam> hrBaseEmployeeInfoIdParamList);
+
+    /**
+     * 获取人员明细详情
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     */
+    HrBaseEmployeeInfo detail(HrBaseEmployeeInfoIdParam hrBaseEmployeeInfoIdParam);
+
+    /**
+     * 获取人员明细详情
+     *
+     * @author 余
+     * @date  2024/11/14 15:02
+     **/
+    HrBaseEmployeeInfo queryEntity(String id);
+}

+ 94 - 0
huimv-farm/src/main/java/vip/xiaonuo/hr/modular/baseemployeeinfo/service/impl/HrBaseEmployeeInfoServiceImpl.java

@@ -0,0 +1,94 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.hr.modular.baseemployeeinfo.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import vip.xiaonuo.common.enums.CommonSortOrderEnum;
+import vip.xiaonuo.common.exception.CommonException;
+import vip.xiaonuo.common.page.CommonPageRequest;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.entity.HrBaseEmployeeInfo;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.mapper.HrBaseEmployeeInfoMapper;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoAddParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoEditParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoIdParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.param.HrBaseEmployeeInfoPageParam;
+import vip.xiaonuo.hr.modular.baseemployeeinfo.service.HrBaseEmployeeInfoService;
+
+import java.util.List;
+
+/**
+ * 人员明细Service接口实现类
+ *
+ * @author 余
+ * @date  2024/11/14 15:02
+ **/
+@Service
+public class HrBaseEmployeeInfoServiceImpl extends ServiceImpl<HrBaseEmployeeInfoMapper, HrBaseEmployeeInfo> implements HrBaseEmployeeInfoService {
+
+    @Override
+    public Page<HrBaseEmployeeInfo> page(HrBaseEmployeeInfoPageParam hrBaseEmployeeInfoPageParam) {
+        QueryWrapper<HrBaseEmployeeInfo> queryWrapper = new QueryWrapper<>();
+        if(ObjectUtil.isAllNotEmpty(hrBaseEmployeeInfoPageParam.getSortField(), hrBaseEmployeeInfoPageParam.getSortOrder())) {
+            CommonSortOrderEnum.validate(hrBaseEmployeeInfoPageParam.getSortOrder());
+            queryWrapper.orderBy(true, hrBaseEmployeeInfoPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
+                    StrUtil.toUnderlineCase(hrBaseEmployeeInfoPageParam.getSortField()));
+        } else {
+            queryWrapper.lambda().orderByAsc(HrBaseEmployeeInfo::getId);
+        }
+        return this.page(CommonPageRequest.defaultPage(), queryWrapper);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void add(HrBaseEmployeeInfoAddParam hrBaseEmployeeInfoAddParam) {
+        HrBaseEmployeeInfo hrBaseEmployeeInfo = BeanUtil.toBean(hrBaseEmployeeInfoAddParam, HrBaseEmployeeInfo.class);
+        this.save(hrBaseEmployeeInfo);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void edit(HrBaseEmployeeInfoEditParam hrBaseEmployeeInfoEditParam) {
+        HrBaseEmployeeInfo hrBaseEmployeeInfo = this.queryEntity(hrBaseEmployeeInfoEditParam.getId());
+        BeanUtil.copyProperties(hrBaseEmployeeInfoEditParam, hrBaseEmployeeInfo);
+        this.updateById(hrBaseEmployeeInfo);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(List<HrBaseEmployeeInfoIdParam> hrBaseEmployeeInfoIdParamList) {
+        // 执行删除
+        this.removeByIds(CollStreamUtil.toList(hrBaseEmployeeInfoIdParamList, HrBaseEmployeeInfoIdParam::getId));
+    }
+
+    @Override
+    public HrBaseEmployeeInfo detail(HrBaseEmployeeInfoIdParam hrBaseEmployeeInfoIdParam) {
+        return this.queryEntity(hrBaseEmployeeInfoIdParam.getId());
+    }
+
+    @Override
+    public HrBaseEmployeeInfo queryEntity(String id) {
+        HrBaseEmployeeInfo hrBaseEmployeeInfo = this.getById(id);
+        if(ObjectUtil.isEmpty(hrBaseEmployeeInfo)) {
+            throw new CommonException("人员明细不存在,id值为:{}", id);
+        }
+        return hrBaseEmployeeInfo;
+    }
+}

+ 6 - 0
huimv-farm/src/main/resources/README.md

@@ -0,0 +1,6 @@
+#druid加密密码
+java -cp druid-1.2.8.jar com.alibaba.druid.filter.config.ConfigTools your password
+
+privateKey:MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAxaJNW11cVWuAUcMMoQtkzRLLCRZjcqO6A33R5jlKiCifWPxjQKU8XufHcW4TukB4EqK/sKEhLS+/H/n1ZSkqZQIDAQABAkAN8PACBOjxgZ7QpbCrX5FhwfSelHy5ZoFFo8d9tQbj6dHOrfwHsRqAkAxGoZ/ivhDKUYf/G7eSwVUqv2H0T1m1AiEA9jRLwNL4/R5HhqfqNVMIF+GbuBhtM+HHPFOoOCR0f4sCIQDNf0ulYZf4DuyDiDuvpWjMtLEuNgnN0LOo1iVKFtM7zwIgfcdfwAXIEEAM2H0mSNG/e7vVeup3t56r02UFtpDhN1sCIQCGhrnnx07sJiDxLoMcRmWn9PY0sU2TvfePJLl+mhiogwIhAPPckbPRPJP52gutyn8GGNV28absrzjA6thi6KAej1aC
+publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMWiTVtdXFVrgFHDDKELZM0SywkWY3KjugN90eY5Sogon1j8Y0ClPF7nx3FuE7pAeBKiv7ChIS0vvx/59WUpKmUCAwEAAQ==
+password:DDnBgo6jKfgn7z+Qhn4/KTbzKzyT/ByFi7JDJA2w6yP/t++OQUjHJyApYeWaN4qVFV3V4fq0HbqTmkyWVj44yA==

Різницю між файлами не показано, бо вона завелика
+ 1048 - 0
huimv-farm/src/main/resources/_sql/snowy_mysql.sql


Різницю між файлами не показано, бо вона завелика
+ 1891 - 0
huimv-farm/src/main/resources/_sql/snowy_oracle.sql


+ 3 - 0
huimv-farm/src/main/resources/_sql/数据库说明.md

@@ -0,0 +1,3 @@
+# 数据库说明
+1.snowy_mysql.sql:mysql版本的主sql文件,如果你是mysql,运行此sql并启动项目即可
+2.snowy_oracle.sql:oracle版本的主sql文件,如果你是oracle,运行此sql并启动项目即可

+ 183 - 0
huimv-farm/src/main/resources/application-test.properties

@@ -0,0 +1,183 @@
+#########################################
+# server configuration
+#########################################
+server.port=82
+
+#########################################
+# spring profiles configuration
+#########################################
+#spring.profiles.active=local
+#spring.profiles.active=test
+#spring.profiles.active=prod
+
+#########################################
+# multipart configurationapplication
+#########################################
+spring.servlet.multipart.max-request-size=100MB
+spring.servlet.multipart.max-file-size=100MB
+
+#########################################
+# datasource configuration
+#########################################
+
+# mysql
+spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowy?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
+spring.datasource.dynamic.datasource.master.url=jdbc:mysql://139.9.172.209:3309/huimv_garden?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
+spring.datasource.dynamic.datasource.master.username=eartag
+spring.datasource.dynamic.datasource.master.password=eartag@2022
+spring.datasource.dynamic.strict=true
+
+# postgres
+#spring.datasource.dynamic.datasource.master.driver-class-name=org.postgresql.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:postgresql://localhost:5432/snowy
+#spring.datasource.dynamic.datasource.master.username=postgres
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# oracle
+#spring.datasource.dynamic.datasource.master.driver-class-name=oracle.jdbc.driver.OracleDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:oracle:thin:@//127.0.0.1:1521/XE?remarksReporting=true
+#spring.datasource.dynamic.datasource.master.username=SNOWY
+#spring.datasource.dynamic.datasource.master.password=12345678
+#spring.datasource.dynamic.strict=true
+
+# mssql
+#spring.datasource.dynamic.datasource.master.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:sqlserver://localhost:1433;DatabaseName=SNOWY
+#spring.datasource.dynamic.datasource.master.username=sa
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# dm database
+#spring.datasource.dynamic.datasource.master.driver-class-name=dm.jdbc.driver.DmDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:dm://localhost:5236/SYSDBA
+#spring.datasource.dynamic.datasource.master.username=SYSDBA
+#spring.datasource.dynamic.datasource.master.password=SYSDBA
+#spring.datasource.dynamic.strict=true
+
+# kingbase database
+#spring.datasource.dynamic.datasource.master.driver-class-name=com.kingbase8.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:kingbase8://localhost:54321/snowy
+#spring.datasource.dynamic.datasource.master.username=SYSTEM
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# druid monitor configuration
+spring.datasource.druid.stat-view-servlet.enabled=true
+spring.datasource.druid.stat-view-servlet.login-username=admin
+spring.datasource.druid.stat-view-servlet.login-password=123456
+
+# druid global configuration
+spring.datasource.dynamic.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMWiTVtdXFVrgFHDDKELZM0SywkWY3KjugN90eY5Sogon1j8Y0ClPF7nx3FuE7pAeBKiv7ChIS0vvx/59WUpKmUCAwEAAQ==
+spring.datasource.dynamic.druid.initial-size=5
+spring.datasource.dynamic.druid.max-active=20
+spring.datasource.dynamic.druid.min-idle=5
+spring.datasource.dynamic.druid.max-wait=60000
+spring.datasource.dynamic.druid.pool-prepared-statements=true
+spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size=20
+spring.datasource.dynamic.druid.validation-query-timeout=2000
+spring.datasource.dynamic.druid.test-on-borrow=false
+spring.datasource.dynamic.druid.test-on-return=false
+spring.datasource.dynamic.druid.test-while-idle=true
+spring.datasource.dynamic.druid.time-between-eviction-runs-millis=60000
+spring.datasource.dynamic.druid.min-evictable-idle-time-millis=300000
+spring.datasource.dynamic.druid.filters=stat
+spring.datasource.dynamic.druid.break-after-acquire-failure=false
+
+#########################################
+# jackson configuration
+#########################################
+spring.jackson.time-zone=GMT+8
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.locale=zh_CN
+
+#########################################
+# redis configuration
+#########################################
+spring.redis.database=3
+spring.redis.host=122.112.224.199
+spring.redis.port=6379
+spring.redis.password=hm123456
+spring.redis.timeout=10s
+
+spring.redis.lettuce.pool.max-active=200
+spring.redis.lettuce.pool.max-wait=-1ms
+spring.redis.lettuce.pool.max-idle=10
+spring.redis.lettuce.pool.min-idle=0
+
+#########################################
+# mybatis-plus configuration
+#########################################
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+mybatis-plus.configuration.jdbc-type-for-null=null
+mybatis-plus.global-config.banner=false
+mybatis-plus.global-config.enable-sql-runner=true
+mybatis-plus.global-config.db-config.id-type=ASSIGN_ID
+mybatis-plus.global-config.db-config.logic-delete-field=DELETE_FLAG
+mybatis-plus.global-config.db-config.logic-delete-value=DELETED
+mybatis-plus.global-config.db-config.logic-not-delete-value=NOT_DELETE
+mybatis-plus.mapper-locations=classpath*:vip/xiaonuo/**/mapping/*.xml
+mybatis-plus.type-handlers-package=vip.xiaonuo.common.handler
+
+#########################################
+# easy-trans configuration
+#########################################
+easy-trans.is-enable-redis=true
+easy-trans.is-enable-global=true
+easy-trans.is-enable-tile=true
+
+#########################################
+# sa-token configuration
+#########################################
+sa-token.token-name=token
+sa-token.timeout=2592000
+sa-token.activity-timeout=-1
+sa-token.is-concurrent=true
+sa-token.is-share=false
+sa-token.max-login-count=-1
+sa-token.token-style=random-32
+sa-token.is-log=false
+sa-token.is-print=false
+
+# sa-token alone-redis configuration
+sa-token.alone-redis.database=2
+sa-token.alone-redis.host=${spring.redis.host}
+sa-token.alone-redis.port=${spring.redis.port}
+sa-token.alone-redis.password=${spring.redis.password}
+sa-token.alone-redis.timeout=${spring.redis.timeout}
+sa-token.alone-redis.lettuce.pool.max-active=${spring.redis.lettuce.pool.max-active}
+sa-token.alone-redis.lettuce.pool.max-wait=${spring.redis.lettuce.pool.max-wait}
+sa-token.alone-redis.lettuce.pool.max-idle=${spring.redis.lettuce.pool.max-idle}
+sa-token.alone-redis.lettuce.pool.min-idle=${spring.redis.lettuce.pool.min-idle}
+
+#########################################
+# knife4j configuration
+#########################################
+knife4j.enable=true
+knife4j.production=false
+knife4j.basic.enable=true
+knife4j.basic.username=admin
+knife4j.basic.password=123456
+knife4j.setting.enableOpenApi=false
+knife4j.setting.enableSwaggerModels=false
+knife4j.setting.enableFooter=false
+knife4j.setting.enableFooterCustom=true
+knife4j.setting.footerCustomContent=Apache License 2.0 | Copyright 2022-[SNOWY](https://www.baiduc.com)
+
+#########################################
+# snowy configuration
+#########################################
+
+# common configuration
+snowy.config.common.front-url=http://localhost:81
+snowy.config.common.backend-url=http://localhost:82
+
+
+img.url=https://img.ifarmcloud.com/images/
+img.basePath=/home/huimv/img/
+img.ip=119.3.44.183
+img.user=huimv
+img.password=!hm123@1
+img.port=22
+

+ 190 - 0
huimv-farm/src/main/resources/application.properties

@@ -0,0 +1,190 @@
+#########################################
+# server configuration
+#########################################
+server.port=82
+
+#########################################
+# spring profiles configuration
+#########################################
+#spring.profiles.active=local
+spring.profiles.active=test
+#spring.profiles.active=prod
+
+#########################################
+# multipart configurationapplication
+#########################################
+spring.servlet.multipart.max-request-size=100MB
+spring.servlet.multipart.max-file-size=100MB
+
+#########################################
+# datasource configuration
+#########################################
+
+# mysql
+spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowy?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
+spring.datasource.dynamic.datasource.master.url=jdbc:mysql://139.9.172.209:3309/huimv_garden?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true
+spring.datasource.dynamic.datasource.master.username=eartag
+spring.datasource.dynamic.datasource.master.password=eartag@2022
+spring.datasource.dynamic.strict=true
+
+# postgres
+#spring.datasource.dynamic.datasource.master.driver-class-name=org.postgresql.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:postgresql://localhost:5432/snowy
+#spring.datasource.dynamic.datasource.master.username=postgres
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# oracle
+#spring.datasource.dynamic.datasource.master.driver-class-name=oracle.jdbc.driver.OracleDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:oracle:thin:@//127.0.0.1:1521/XE?remarksReporting=true
+#spring.datasource.dynamic.datasource.master.username=SNOWY
+#spring.datasource.dynamic.datasource.master.password=12345678
+#spring.datasource.dynamic.strict=true
+
+# mssql
+#spring.datasource.dynamic.datasource.master.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:sqlserver://localhost:1433;DatabaseName=SNOWY
+#spring.datasource.dynamic.datasource.master.username=sa
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# dm database
+#spring.datasource.dynamic.datasource.master.driver-class-name=dm.jdbc.driver.DmDriver
+#spring.datasource.dynamic.datasource.master.url=jdbc:dm://localhost:5236/SYSDBA
+#spring.datasource.dynamic.datasource.master.username=SYSDBA
+#spring.datasource.dynamic.datasource.master.password=SYSDBA
+#spring.datasource.dynamic.strict=true
+
+# kingbase database
+#spring.datasource.dynamic.datasource.master.driver-class-name=com.kingbase8.Driver
+#spring.datasource.dynamic.datasource.master.url=jdbc:kingbase8://localhost:54321/snowy
+#spring.datasource.dynamic.datasource.master.username=SYSTEM
+#spring.datasource.dynamic.datasource.master.password=123456
+#spring.datasource.dynamic.strict=true
+
+# druid monitor configuration
+spring.datasource.druid.stat-view-servlet.enabled=true
+spring.datasource.druid.stat-view-servlet.login-username=admin
+spring.datasource.druid.stat-view-servlet.login-password=123456
+
+# druid global configuration
+spring.datasource.dynamic.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMWiTVtdXFVrgFHDDKELZM0SywkWY3KjugN90eY5Sogon1j8Y0ClPF7nx3FuE7pAeBKiv7ChIS0vvx/59WUpKmUCAwEAAQ==
+spring.datasource.dynamic.druid.initial-size=5
+spring.datasource.dynamic.druid.max-active=20
+spring.datasource.dynamic.druid.min-idle=5
+spring.datasource.dynamic.druid.max-wait=60000
+spring.datasource.dynamic.druid.pool-prepared-statements=true
+spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size=20
+spring.datasource.dynamic.druid.validation-query-timeout=2000
+spring.datasource.dynamic.druid.test-on-borrow=false
+spring.datasource.dynamic.druid.test-on-return=false
+spring.datasource.dynamic.druid.test-while-idle=true
+spring.datasource.dynamic.druid.time-between-eviction-runs-millis=60000
+spring.datasource.dynamic.druid.min-evictable-idle-time-millis=300000
+spring.datasource.dynamic.druid.filters=stat
+spring.datasource.dynamic.druid.break-after-acquire-failure=false
+
+#########################################
+# jackson configuration
+#########################################
+spring.jackson.time-zone=GMT+8
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.locale=zh_CN
+
+#########################################
+# redis configuration
+#########################################
+spring.redis.database=1
+spring.redis.host=122.112.224.199
+spring.redis.port=6379
+spring.redis.password=hm123456
+spring.redis.timeout=10s
+
+spring.redis.lettuce.pool.max-active=200
+spring.redis.lettuce.pool.max-wait=-1ms
+spring.redis.lettuce.pool.max-idle=10
+spring.redis.lettuce.pool.min-idle=0
+
+#########################################
+# mybatis-plus configuration
+#########################################
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+mybatis-plus.configuration.jdbc-type-for-null=null
+mybatis-plus.global-config.banner=false
+mybatis-plus.global-config.enable-sql-runner=true
+mybatis-plus.global-config.db-config.id-type=ASSIGN_ID
+mybatis-plus.global-config.db-config.logic-delete-field=DELETE_FLAG
+mybatis-plus.global-config.db-config.logic-delete-value=DELETED
+mybatis-plus.global-config.db-config.logic-not-delete-value=NOT_DELETE
+mybatis-plus.mapper-locations=classpath*:vip/xiaonuo/**/mapping/*.xml
+mybatis-plus.type-handlers-package=vip.xiaonuo.common.handler
+
+#########################################
+# easy-trans configuration
+#########################################
+easy-trans.is-enable-redis=true
+easy-trans.is-enable-global=true
+easy-trans.is-enable-tile=true
+
+#########################################
+# sa-token configuration
+#########################################
+sa-token.token-name=token
+sa-token.timeout=2592000
+sa-token.activity-timeout=-1
+sa-token.is-concurrent=true
+sa-token.is-share=false
+sa-token.max-login-count=-1
+sa-token.token-style=random-32
+sa-token.is-log=false
+sa-token.is-print=false
+
+# sa-token alone-redis configuration
+sa-token.alone-redis.database=2
+sa-token.alone-redis.host=${spring.redis.host}
+sa-token.alone-redis.port=${spring.redis.port}
+sa-token.alone-redis.password=${spring.redis.password}
+sa-token.alone-redis.timeout=${spring.redis.timeout}
+sa-token.alone-redis.lettuce.pool.max-active=${spring.redis.lettuce.pool.max-active}
+sa-token.alone-redis.lettuce.pool.max-wait=${spring.redis.lettuce.pool.max-wait}
+sa-token.alone-redis.lettuce.pool.max-idle=${spring.redis.lettuce.pool.max-idle}
+sa-token.alone-redis.lettuce.pool.min-idle=${spring.redis.lettuce.pool.min-idle}
+
+#########################################
+# knife4j configuration
+#########################################
+knife4j.enable=true
+knife4j.production=false
+knife4j.basic.enable=true
+knife4j.basic.username=admin
+knife4j.basic.password=123456
+knife4j.setting.enableOpenApi=false
+knife4j.setting.enableSwaggerModels=false
+knife4j.setting.enableFooter=false
+knife4j.setting.enableFooterCustom=true
+knife4j.setting.footerCustomContent=Apache License 2.0 | Copyright 2022-[SNOWY](https://www.baiduc.com)
+
+#########################################
+# snowy configuration
+#########################################
+
+# common configuration
+snowy.config.common.front-url=http://localhost:81
+snowy.config.common.backend-url=http://localhost:82
+
+
+img.url=https://img.ifarmcloud.com/images/
+img.basePath=/home/huimv/img/
+img.ip=119.3.44.183
+img.user=huimv
+img.password=!hm123@1
+img.port=22
+
+#img:
+#url: https://img.ifarmcloud.com/images/
+#basePath: /home/huimv/img/
+#ip: 139.9.172.209
+#user:  huimv
+#password: "!hm123@1"
+#port: 22

+ 118 - 0
huimv-farm/src/main/resources/logback-spring.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <!--日志格式应用spring boot默认的格式,也可以自己更改-->
+    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
+
+    <!--定义日志存放的位置,默认存放在项目启动的相对路径的目录-->
+    <springProperty scope="context" name="LOG_PATH" source="log.path" defaultValue="app-log"/>
+
+    <!-- ****************************************************************************************** -->
+    <!-- ****************************** 本地开发只在控制台打印日志 ************************************ -->
+    <!-- ****************************************************************************************** -->
+    <springProfile name="local">
+        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+                <charset>utf-8</charset>
+            </encoder>
+        </appender>
+
+        <!--默认所有的包以info-->
+        <root level="info">
+            <appender-ref ref="STDOUT"/>
+        </root>
+
+        <!--各个服务的包在本地执行的时候,打开debug模式-->
+        <logger name="vip.xiaonuo" level="info" additivity="false">
+            <appender-ref ref="STDOUT"/>
+        </logger>
+    </springProfile>
+
+    <!-- ********************************************************************************************** -->
+    <!-- **** 放到服务器上不管在什么环境都只在文件记录日志,控制台(catalina.out)打印logback捕获不到的日志 **** -->
+    <!-- ********************************************************************************************** -->
+    <springProfile name="!local">
+
+        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+                <charset>utf-8</charset>
+            </encoder>
+        </appender>
+
+        <!-- 日志记录器,日期滚动记录 -->
+        <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+            <!-- 正在记录的日志文件的路径及文件名 -->
+            <file>${LOG_PATH}/log_error.log</file>
+
+            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
+                <!-- 归档的日志文件的路径,%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+                <fileNamePattern>${LOG_PATH}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+
+                <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+                命名日志文件,例如log-error-2013-12-21.0.log -->
+                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                    <maxFileSize>10MB</maxFileSize>
+                </timeBasedFileNamingAndTriggeringPolicy>
+            </rollingPolicy>
+
+            <!-- 追加方式记录日志 -->
+            <append>true</append>
+
+            <!-- 日志文件的格式 -->
+            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+                <charset>utf-8</charset>
+            </encoder>
+
+            <!-- 此日志文件只记录error级别的 -->
+            <filter class="ch.qos.logback.classic.filter.LevelFilter">
+                <level>error</level>
+                <onMatch>ACCEPT</onMatch>
+                <onMismatch>DENY</onMismatch>
+            </filter>
+        </appender>
+
+        <!-- 日志记录器,日期滚动记录 -->
+        <appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+            <!-- 正在记录的日志文件的路径及文件名 -->
+            <file>${LOG_PATH}/log_total.log</file>
+
+            <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
+                <!-- 归档的日志文件的路径,%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
+                <fileNamePattern>${LOG_PATH}/total/log-total-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+
+                <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
+                命名日志文件,例如log-error-2013-12-21.0.log -->
+                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                    <maxFileSize>10MB</maxFileSize>
+                </timeBasedFileNamingAndTriggeringPolicy>
+            </rollingPolicy>
+
+            <!-- 追加方式记录日志 -->
+            <append>true</append>
+
+            <!-- 日志文件的格式 -->
+            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+                <charset>utf-8</charset>
+            </encoder>
+        </appender>
+
+        <!--记录到文件时,记录两类一类是error日志,一个是所有日志-->
+        <root level="info">
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="FILE_ERROR"/>
+            <appender-ref ref="FILE_ALL"/>
+        </root>
+
+    </springProfile>
+
+</configuration>

+ 34 - 0
huimv-farm/src/test/java/vip/xiaonuo/MainTest.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * 主测试类
+ *
+ * @author xuyuxiang
+ * @date 2022/9/17 17:09
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+public class MainTest {
+
+    @Test
+    public void test() {
+
+    }
+}

+ 25 - 0
huimv-farm/src/test/java/vip/xiaonuo/Test.java

@@ -0,0 +1,25 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo;
+
+/**
+ * TestMain方法
+ *
+ * @author xuyuxiang
+ * @date 2022/9/17 17:10
+ */
+public class Test {
+    public static void main(String[] args) {
+
+    }
+}

+ 707 - 0
pom.xml

@@ -0,0 +1,707 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>huimv.garden</groupId>
+    <artifactId>garden</artifactId>
+    <name>snowy</name>
+    <version>2.0.0</version>
+    <description>snowy快速开发平台</description>
+    <packaging>pom</packaging>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.5.12</version>
+    </parent>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <snowy.version>2.0.0</snowy.version>
+        <spring-framework.version>5.3.26</spring-framework.version>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <!-- 锁定依赖版本号 -->
+        <ali.oss.version>3.14.0</ali.oss.version>
+        <aliyun.sdk.dm.version>3.3.1</aliyun.sdk.dm.version>
+        <aliyun.sdk.dysmsapi.version>2.0.9</aliyun.sdk.dysmsapi.version>
+        <aliyun.sdk.ecs.version>3.1.0</aliyun.sdk.ecs.version>
+        <bcprov.jdk15on.version>1.70</bcprov.jdk15on.version>
+        <beetl.version>1.2.40.Beetl.RELEASE</beetl.version>
+        <checker.qual.version>3.31.0</checker.qual.version>
+        <commons.beanutils.version>1.9.4</commons.beanutils.version>
+        <commons.compress.version>1.22</commons.compress.version>
+        <commons.pool2.version>2.11.1</commons.pool2.version>
+        <druid.version>1.2.9</druid.version>
+        <dynamic.datasource.version>3.5.1</dynamic.datasource.version>
+        <easy.trans.version>2.1.7</easy.trans.version>
+        <easyexcel.version>3.2.1</easyexcel.version>
+        <easypoi.version>4.3.0</easypoi.version>
+        <fastjson.version>2.0.24</fastjson.version>
+        <gson.version>2.8.9</gson.version>
+        <guava.version>31.1-jre</guava.version>
+        <hutool.version>5.8.12</hutool.version>
+        <ip2region.version>2.6.3</ip2region.version>
+        <jackson.annotations.version>2.14.2</jackson.annotations.version>
+        <jackson.core.version>2.14.2</jackson.core.version>
+        <jackson.databind.version>2.14.2</jackson.databind.version>
+        <jackson.datatype.jdk8.version>2.14.2</jackson.datatype.jdk8.version>
+        <jackson.datatype.jsr310.version>2.14.2</jackson.datatype.jsr310.version>
+        <jackson.module.parameter.names.version>2.14.2</jackson.module.parameter.names.version>
+        <javax.mail.version>1.6.2</javax.mail.version>
+        <jettison.version>1.5.4</jettison.version>
+        <junit.version>4.13.2</junit.version>
+        <just.auth.version>1.16.5</just.auth.version>
+        <knife4j.version>2.0.9</knife4j.version>
+        <logback.classic.version>1.2.0</logback.classic.version>
+        <lombok.versin>1.18.22</lombok.versin>
+        <minio.version>8.5.2</minio.version>
+        <mssql.connector.java.version>9.2.1.jre8</mssql.connector.java.version>
+        <mybatis.plus.version>3.5.3.1</mybatis.plus.version>
+        <mybatis.version>3.5.10</mybatis.version>
+        <mysql.connector.java.version>8.0.28</mysql.connector.java.version>
+        <netty.common.version>4.1.89.Final</netty.common.version>
+        <netty.handler.version>4.1.89.Final</netty.handler.version>
+        <okhttp3.version>4.10.0</okhttp3.version>
+        <okio.version>3.3.0</okio.version>
+        <dm.connector.java.version>8.1.2.192</dm.connector.java.version>
+        <kingbase.connector.java.version>8.6.0</kingbase.connector.java.version>
+        <oracle.connector.java.version>21.5.0.0</oracle.connector.java.version>
+        <oracle.nls.orai18n.version>19.7.0.0</oracle.nls.orai18n.version>
+        <oshi.core.version>6.2.2</oshi.core.version>
+        <pinyin.version>2.5.1</pinyin.version>
+        <postgres.connector.java.version>42.2.25</postgres.connector.java.version>
+        <protobuf.java.version>3.21.12</protobuf.java.version>
+        <sa.token.version>1.31.0</sa.token.version>
+        <smcrypto.version>0.3.2</smcrypto.version>
+        <snakeyaml.version>2.0</snakeyaml.version>
+        <spring.context.version>5.3.19</spring.context.version>
+        <spring.security.crypto.version>6.0.2</spring.security.crypto.version>
+        <springfox.swagger2.version>2.10.5</springfox.swagger2.version>
+        <ten.cos.version>5.6.68</ten.cos.version>
+        <ten.sdk.ses.version>3.1.455</ten.sdk.ses.version>
+        <ten.sdk.sms.version>3.1.455</ten.sdk.sms.version>
+        <tomcat.embed.core.version>9.0.72</tomcat.embed.core.version>
+    </properties>
+
+    <modules>
+        <!-- 基础通用规则模块 -->
+        <module>snowy-common</module>
+
+        <!-- 插件模块 -->
+        <module>snowy-plugin</module>
+
+        <!-- 插件API接口模块 -->
+        <module>snowy-plugin-api</module>
+
+        <!-- 主启动模块 -->
+        <module>huimv-farm</module>
+
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- snowy-common -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-common</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-auth-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-auth-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-biz-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-biz-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-client-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-client-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-dev-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-dev-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-gen-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-gen-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-mobile-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-mobile-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-sys-api -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-sys-api</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-auth -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-auth</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-biz -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-biz</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-client -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-client</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-dev -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-dev</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-gen -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-gen</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-mobile -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-mobile</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- snowy-plugin-sys -->
+            <dependency>
+                <groupId>vip.xiaonuo</groupId>
+                <artifactId>snowy-plugin-sys</artifactId>
+                <version>${snowy.version}</version>
+            </dependency>
+
+            <!-- lombok -->
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.versin}</version>
+            </dependency>
+
+            <!-- druid -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+
+            <!-- mybatis -->
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>${mybatis.version}</version>
+            </dependency>
+
+            <!-- mybatis-plus-core -->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-core</artifactId>
+                <version>${mybatis.plus.version}</version>
+            </dependency>
+
+            <!-- mybatis-plus -->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>${mybatis.plus.version}</version>
+            </dependency>
+
+            <!-- easy-trans -->
+            <dependency>
+                <groupId>com.fhs-opensource</groupId>
+                <artifactId>easy-trans-spring-boot-starter</artifactId>
+                <version>${easy.trans.version}</version>
+            </dependency>
+
+            <!-- easy-trans-mybatis-plus-extend -->
+            <dependency>
+                <groupId>com.fhs-opensource</groupId>
+                <artifactId>easy-trans-mybatis-plus-extend</artifactId>
+                <version>${easy.trans.version}</version>
+            </dependency>
+
+            <!-- redis -->
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-pool2</artifactId>
+                <version>${commons.pool2.version}</version>
+            </dependency>
+
+            <!-- okhttp -->
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>${okhttp3.version}</version>
+            </dependency>
+
+            <!-- okio -->
+            <dependency>
+                <groupId>com.squareup.okio</groupId>
+                <artifactId>okio</artifactId>
+                <version>${okio.version}</version>
+            </dependency>
+
+            <!-- hutool -->
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-all</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <!-- pinyin4j -->
+            <dependency>
+                <groupId>com.belerweb</groupId>
+                <artifactId>pinyin4j</artifactId>
+                <version>${pinyin.version}</version>
+            </dependency>
+
+            <!-- ip2region -->
+            <dependency>
+                <groupId>org.lionsoul</groupId>
+                <artifactId>ip2region</artifactId>
+                <version>${ip2region.version}</version>
+            </dependency>
+
+            <!-- knife4j -->
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+
+            <!-- easy-poi -->
+            <dependency>
+                <groupId>cn.afterturn</groupId>
+                <artifactId>easypoi-spring-boot-starter</artifactId>
+                <version>${easypoi.version}</version>
+            </dependency>
+
+            <!-- sm-crypto -->
+            <dependency>
+                <groupId>com.antherd</groupId>
+                <artifactId>sm-crypto</artifactId>
+                <version>${smcrypto.version}</version>
+            </dependency>
+
+            <!-- easyexcel -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easyexcel.version}</version>
+            </dependency>
+
+            <!-- sa-token-core -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-core</artifactId>
+                <version>${sa.token.version}</version>
+            </dependency>
+
+            <!-- sa-token -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-spring-boot-starter</artifactId>
+                <version>${sa.token.version}</version>
+            </dependency>
+
+            <!-- sa-token 整合 redis (使用jackson序列化方式) -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-dao-redis-jackson</artifactId>
+                <version>${sa.token.version}</version>
+            </dependency>
+
+            <!-- Sa-Token插件:权限缓存与业务缓存分离 -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-alone-redis</artifactId>
+                <version>${sa.token.version}</version>
+            </dependency>
+
+            <!-- Sa-Token 插件:整合SSO -->
+            <dependency>
+                <groupId>cn.dev33</groupId>
+                <artifactId>sa-token-sso</artifactId>
+                <version>${sa.token.version}</version>
+            </dependency>
+
+            <!-- JustAuth 第三方登录 -->
+            <dependency>
+                <groupId>me.zhyd.oauth</groupId>
+                <artifactId>JustAuth</artifactId>
+                <version>${just.auth.version}</version>
+            </dependency>
+
+            <!-- beetl模板引擎 -->
+            <dependency>
+                <groupId>com.ibeetl</groupId>
+                <artifactId>beetl-framework-starter</artifactId>
+                <version>${beetl.version}</version>
+            </dependency>
+
+            <!--腾讯云上传文件客户端-->
+            <dependency>
+                <groupId>com.qcloud</groupId>
+                <artifactId>cos_api</artifactId>
+                <version>${ten.cos.version}</version>
+            </dependency>
+
+            <!--阿里云上传文件客户端-->
+            <dependency>
+                <groupId>com.aliyun.oss</groupId>
+                <artifactId>aliyun-sdk-oss</artifactId>
+                <version>${ali.oss.version}</version>
+            </dependency>
+
+            <!--minio上传文件客户端-->
+            <dependency>
+                <groupId>io.minio</groupId>
+                <artifactId>minio</artifactId>
+                <version>${minio.version}</version>
+            </dependency>
+
+            <!--java邮件发送-->
+            <dependency>
+                <groupId>com.sun.mail</groupId>
+                <artifactId>javax.mail</artifactId>
+                <version>${javax.mail.version}</version>
+            </dependency>
+
+            <!--阿里云邮件发送-->
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>aliyun-java-sdk-dm</artifactId>
+                <version>${aliyun.sdk.dm.version}</version>
+            </dependency>
+
+            <!-- 腾讯云邮件发送 -->
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java-ses</artifactId>
+                <version>${ten.sdk.ses.version}</version>
+            </dependency>
+
+            <!--阿里云短信发送-->
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>dysmsapi20170525</artifactId>
+                <version>${aliyun.sdk.dysmsapi.version}</version>
+            </dependency>
+
+            <!--腾讯云短信发送-->
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java-sms</artifactId>
+                <version>${ten.sdk.sms.version}</version>
+            </dependency>
+
+            <!--系统硬件信息-->
+            <dependency>
+                <groupId>com.github.oshi</groupId>
+                <artifactId>oshi-core</artifactId>
+                <version>${oshi.core.version}</version>
+            </dependency>
+
+            <!-- junit -->
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>${junit.version}</version>
+                <scope>test</scope>
+            </dependency>
+
+            <!-- logback-classic -->
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>${logback.classic.version}</version>
+            </dependency>
+
+            <!-- gson -->
+            <dependency>
+                <groupId>com.google.code.gson</groupId>
+                <artifactId>gson</artifactId>
+                <version>${gson.version}</version>
+            </dependency>
+
+            <!-- guava -->
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
+
+            <!-- netty-common -->
+            <dependency>
+                <groupId>io.netty</groupId>
+                <artifactId>netty-common</artifactId>
+                <version>${netty.common.version}</version>
+            </dependency>
+
+            <!-- netty-common -->
+            <dependency>
+                <groupId>io.netty</groupId>
+                <artifactId>netty-handler</artifactId>
+                <version>${netty.handler.version}</version>
+            </dependency>
+
+            <!-- jettison -->
+            <dependency>
+                <groupId>org.codehaus.jettison</groupId>
+                <artifactId>jettison</artifactId>
+                <version>${jettison.version}</version>
+            </dependency>
+
+            <!-- snakeyaml -->
+            <dependency>
+                <groupId>org.yaml</groupId>
+                <artifactId>snakeyaml</artifactId>
+                <version>${snakeyaml.version}</version>
+            </dependency>
+
+            <!-- spring-context -->
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-context</artifactId>
+                <version>${spring.context.version}</version>
+            </dependency>
+
+            <!-- spring-security-crypto -->
+            <dependency>
+                <groupId>org.springframework.security</groupId>
+                <artifactId>spring-security-crypto</artifactId>
+                <version>${spring.security.crypto.version}</version>
+            </dependency>
+
+            <!-- springfox-swagger2 -->
+            <dependency>
+                <groupId>io.springfox</groupId>
+                <artifactId>springfox-swagger2</artifactId>
+                <version>${springfox.swagger2.version}</version>
+            </dependency>
+
+            <!-- tomcat-embed-core -->
+            <dependency>
+                <groupId>org.apache.tomcat.embed</groupId>
+                <artifactId>tomcat-embed-core</artifactId>
+                <version>${tomcat.embed.core.version}</version>
+            </dependency>
+
+            <!-- fastjson -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+
+            <!-- jackson-annotations -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-annotations</artifactId>
+                <version>${jackson.annotations.version}</version>
+            </dependency>
+
+            <!-- jackson-core -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-core</artifactId>
+                <version>${jackson.core.version}</version>
+            </dependency>
+
+            <!-- jackson-databind -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>${jackson.databind.version}</version>
+            </dependency>
+
+            <!-- jackson-datatype -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.datatype</groupId>
+                <artifactId>jackson-datatype-jdk8</artifactId>
+                <version>${jackson.datatype.jdk8.version}</version>
+            </dependency>
+
+            <!-- jackson-jsr310 -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.datatype</groupId>
+                <artifactId>jackson-datatype-jsr310</artifactId>
+                <version>${jackson.datatype.jsr310.version}</version>
+            </dependency>
+
+            <!-- jackson-module-parameter-names -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.module</groupId>
+                <artifactId>jackson-module-parameter-names</artifactId>
+                <version>${jackson.module.parameter.names.version}</version>
+            </dependency>
+
+            <!-- commons-beanutils -->
+            <dependency>
+                <groupId>commons-beanutils</groupId>
+                <artifactId>commons-beanutils</artifactId>
+                <version>${commons.beanutils.version}</version>
+            </dependency>
+
+            <!-- commons-compress -->
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-compress</artifactId>
+                <version>${commons.compress.version}</version>
+            </dependency>
+
+            <!-- protobuf-java -->
+            <dependency>
+                <groupId>com.google.protobuf</groupId>
+                <artifactId>protobuf-java</artifactId>
+                <version>${protobuf.java.version}</version>
+            </dependency>
+
+            <!-- checker-qual -->
+            <dependency>
+                <groupId>org.checkerframework</groupId>
+                <artifactId>checker-qual</artifactId>
+                <version>${checker.qual.version}</version>
+            </dependency>
+
+            <!-- bcprov-jdk15on -->
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk15on</artifactId>
+                <version>${bcprov.jdk15on.version}</version>
+            </dependency>
+
+            <!-- dynamic-datasource -->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+                <version>${dynamic.datasource.version}</version>
+            </dependency>
+
+            <!-- mysql -->
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${mysql.connector.java.version}</version>
+            </dependency>
+
+            <!-- postgresql -->
+            <!--<dependency>
+                <groupId>org.postgresql</groupId>
+                <artifactId>postgresql</artifactId>
+                <version>${postgres.connector.java.version}</version>
+            </dependency>-->
+
+            <!-- 达梦数据库 -->
+            <!--<dependency>
+                <groupId>com.dameng</groupId>
+                <artifactId>DmJdbcDriver18</artifactId>
+                <version>${dm.connector.java.version}</version>
+            </dependency>-->
+
+            <!-- 人大金仓数据库 -->
+            <!--<dependency>
+                <groupId>cn.com.kingbase</groupId>
+                <artifactId>kingbase8</artifactId>
+                <version>${kingbase.connector.java.version}</version>
+            </dependency>-->
+
+            <!-- oracle -->
+            <!--<dependency>
+                <groupId>com.oracle.database.jdbc</groupId>
+                <artifactId>ojdbc8</artifactId>
+                <version>${oracle.connector.java.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.oracle.database.nls</groupId>
+                <artifactId>orai18n</artifactId>
+                <version>${oracle.nls.orai18n.version}</version>
+            </dependency>-->
+
+            <!-- mssql -->
+            <!--<dependency>
+                <groupId>com.microsoft.sqlserver</groupId>
+                <artifactId>mssql-jdbc</artifactId>
+                <version>${mssql.connector.java.version}</version>
+            </dependency>-->
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.7.0</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.0.1</version>
+                <configuration>
+                    <attach>true</attach>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.xml</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>

+ 1 - 0
snowy-common/READM.md

@@ -0,0 +1 @@
+# 基础通用模块

+ 182 - 0
snowy-common/pom.xml

@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>huimv.garden</groupId>
+        <artifactId>garden</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-common</artifactId>
+    <packaging>jar</packaging>
+    <description>基础通用模块</description>
+
+    <dependencies>
+        <!-- validation -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <!-- web -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- aop -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!-- processor -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <!-- redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <!-- lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!-- druid -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- mybatis-plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.54</version>
+        </dependency>
+
+        <!-- easy-trans -->
+        <dependency>
+            <groupId>com.fhs-opensource</groupId>
+            <artifactId>easy-trans-spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>mybatis-plus-annotation</artifactId>
+                    <groupId>com.baomidou</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>springfox-schema</artifactId>
+                    <groupId>io.springfox</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>error_prone_annotations</artifactId>
+                    <groupId>com.google.errorprone</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- easy-trans-mybatis-plus-extend -->
+        <dependency>
+            <groupId>com.fhs-opensource</groupId>
+            <artifactId>easy-trans-mybatis-plus-extend</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>mybatis-plus-extension</artifactId>
+                    <groupId>com.baomidou</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- redis -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <!-- jackson-core -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+
+        <!-- jackson-databind -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <!-- hutool -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <!-- pinyin4j -->
+        <dependency>
+            <groupId>com.belerweb</groupId>
+            <artifactId>pinyin4j</artifactId>
+        </dependency>
+
+        <!-- ip2region -->
+        <dependency>
+            <groupId>org.lionsoul</groupId>
+            <artifactId>ip2region</artifactId>
+        </dependency>
+
+        <!-- knife4j -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- easy-poi -->
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>javassist</artifactId>
+                    <groupId>org.javassist</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>poi</artifactId>
+                    <groupId>org.apache.poi</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>poi-ooxml</artifactId>
+                    <groupId>org.apache.poi</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>poi-ooxml-schemas</artifactId>
+                    <groupId>org.apache.poi</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- sm-crypto -->
+        <dependency>
+            <groupId>com.antherd</groupId>
+            <artifactId>sm-crypto</artifactId>
+        </dependency>
+
+        <!-- easyexcel -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 32 - 0
snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonLog.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义日志注解
+ *
+ * @author xuyuxiang
+ * @date 2022/6/20 14:25
+ **/
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface CommonLog {
+
+    /**
+     * 日志的名称,例如:"修改菜单"
+     */
+    String value() default "未命名";
+}

+ 32 - 0
snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonNoRepeat.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义节流防抖注解
+ *
+ * @author xuyuxiang
+ * @date 2022/6/20 14:25
+ **/
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface CommonNoRepeat {
+
+    /**
+     * 间隔时间(ms),小于此时间视为重复提交,默认5000ms
+     */
+    int interval() default 5000;
+}

+ 35 - 0
snowy-common/src/main/java/vip/xiaonuo/common/annotation/CommonWrapper.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.annotation;
+
+import vip.xiaonuo.common.pojo.CommonWrapperInterface;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义包装注解,对响应结果包装
+ *
+ * @author xuyuxiang
+ * @date 2022/9/15 21:12
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface CommonWrapper {
+
+    /**
+     * 具体包装类
+     */
+    Class<? extends CommonWrapperInterface<?>>[] value();
+}

+ 13 - 0
snowy-common/src/main/java/vip/xiaonuo/common/annotation/Test.java

@@ -0,0 +1,13 @@
+package vip.xiaonuo.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Test {
+    int age() default 18;
+    String name();
+}

+ 86 - 0
snowy-common/src/main/java/vip/xiaonuo/common/cache/CommonCacheOperator.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.cache;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 通用Redis缓存操作器
+ *
+ * @author xuyuxiang
+ * @date 2022/6/21 16:00
+ **/
+@Component
+public class CommonCacheOperator {
+
+    /** 所有缓存Key的前缀 */
+    private static final String CACHE_KEY_PREFIX = "Cache:";
+
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+
+    public void put(String key, Object value) {
+        redisTemplate.boundValueOps(CACHE_KEY_PREFIX + key).set(value);
+    }
+
+    public void put(String key, Object value, long timeoutSeconds) {
+        redisTemplate.boundValueOps(CACHE_KEY_PREFIX + key).set(value, timeoutSeconds, TimeUnit.SECONDS);
+    }
+
+    public Object get(String key) {
+        return redisTemplate.boundValueOps(CACHE_KEY_PREFIX + key).get();
+    }
+
+    public void remove(String... key) {
+        ArrayList<String> keys = CollectionUtil.toList(key);
+        List<String> withPrefixKeys = keys.stream().map(i -> CACHE_KEY_PREFIX + i).collect(Collectors.toList());
+        redisTemplate.delete(withPrefixKeys);
+    }
+
+    public Collection<String> getAllKeys() {
+        Set<String> keys = redisTemplate.keys(CACHE_KEY_PREFIX + "*");
+        if (keys != null) {
+            // 去掉缓存key的common prefix前缀
+            return keys.stream().map(key -> StrUtil.removePrefix(key, CACHE_KEY_PREFIX)).collect(Collectors.toSet());
+        } else {
+            return CollectionUtil.newHashSet();
+        }
+    }
+
+    public Collection<Object> getAllValues() {
+        Set<String> keys = redisTemplate.keys(CACHE_KEY_PREFIX + "*");
+        if (keys != null) {
+            return redisTemplate.opsForValue().multiGet(keys);
+        } else {
+            return CollectionUtil.newArrayList();
+        }
+    }
+
+    public Map<String, Object> getAllKeyValues() {
+        Collection<String> allKeys = this.getAllKeys();
+        HashMap<String, Object> results = MapUtil.newHashMap();
+        for (String key : allKeys) {
+            results.put(key, this.get(key));
+        }
+        return results;
+    }
+}

+ 31 - 0
snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonDeleteFlagEnum.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.enums;
+
+import lombok.Getter;
+
+/**
+ * 通用删除标志枚举
+ *
+ * @author xuyuxiang
+ * @date 2021/10/11 14:02
+ **/
+@Getter
+public enum CommonDeleteFlagEnum {
+
+    /** 未删除 */
+    NOT_DELETE,
+
+    /** 已删除 */
+    DELETED
+}

+ 42 - 0
snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonExceptionEnum.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.enums;
+
+import lombok.Getter;
+
+/**
+ * 异常码枚举
+ *
+ * @author xuyuxiang
+ * @date 2022/8/15 16:09
+ **/
+@Getter
+public enum CommonExceptionEnum {
+
+    OK200(200, "请求成功"),
+    ERROR401(401, "未登录"),
+    ERROR403(403, "无权限"),
+    ERROR404(404, "路径不存在"),
+    ERROR405(405, "请求方法不正确"),
+    ERROR415(415, "参数传递异常"),
+    ERROR500(500, "业务异常");
+
+    private final Integer code;
+
+    private final String message;
+
+    CommonExceptionEnum(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+}

+ 45 - 0
snowy-common/src/main/java/vip/xiaonuo/common/enums/CommonSortOrderEnum.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.enums;
+
+import lombok.Getter;
+import vip.xiaonuo.common.exception.CommonException;
+
+/**
+ * 通用排序方式枚举
+ *
+ * @author xuyuxiang
+ * @date 2022/7/13 17:48
+ **/
+@Getter
+public enum CommonSortOrderEnum {
+
+    /** 升序 */
+    ASC("ASCEND"),
+
+    /** 降序 */
+    DESC("DESCEND");
+
+    private final String value;
+
+    CommonSortOrderEnum(String value) {
+        this.value = value.toUpperCase();
+    }
+
+    public static void validate(String value) {
+        boolean flag = ASC.getValue().toLowerCase().equals(value) || DESC.getValue().toLowerCase().equals(value);
+        if(!flag) {
+            throw new CommonException("不支持该排序方式:{}", value);
+        }
+    }
+}

+ 112 - 0
snowy-common/src/main/java/vip/xiaonuo/common/excel/CommonExcelCustomMergeStrategy.java

@@ -0,0 +1,112 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.excel;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.merge.AbstractMergeStrategy;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * EasyExcel自定义合并策略 该类继承了AbstractMergeStrategy抽象合并策略,需要重写merge()方法
+ *
+ * @author xuyuxiang
+ * @date 2023/3/6 13:21
+ **/
+public class CommonExcelCustomMergeStrategy extends AbstractMergeStrategy {
+
+    /**
+     * 分组,每几行合并一次
+     */
+    private final List<Integer> exportFieldGroupCountList;
+
+    /**
+     * 目标合并列index
+     */
+    private final Integer targetColumnIndex;
+
+    /**
+     * 需要开始合并单元格的首行index
+     */
+    private Integer rowIndex;
+
+    /**
+     * exportDataList为待合并目标列的值
+     *
+     * @author xuyuxiang
+     * @date 2023/3/6 13:23
+     **/
+    public CommonExcelCustomMergeStrategy(List<String> exportDataList, Integer targetColumnIndex) {
+        this.exportFieldGroupCountList = getGroupCountList(exportDataList);
+        this.targetColumnIndex = targetColumnIndex;
+    }
+
+
+    @Override
+    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
+
+        if (null == rowIndex) {
+            rowIndex = cell.getRowIndex();
+        }
+        // 仅从首行以及目标列的单元格开始合并,忽略其他
+        if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) {
+            mergeGroupColumn(sheet);
+        }
+    }
+
+    private void mergeGroupColumn(Sheet sheet) {
+        int rowCount = rowIndex;
+        for (Integer count : exportFieldGroupCountList) {
+            if(count == 1) {
+                rowCount += count;
+                continue ;
+            }
+            // 合并单元格
+            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex);
+            sheet.addMergedRegionUnsafe(cellRangeAddress);
+            rowCount += count;
+        }
+    }
+
+    /*
+     * 该方法将目标列根据值是否相同连续可合并,存储可合并的行数
+     *
+     * @author xuyuxiang
+     * @date 2023/3/6 13:23
+     **/
+    private List<Integer> getGroupCountList(List<String> exportDataList){
+        if (CollectionUtils.isEmpty(exportDataList)) {
+            return new ArrayList<>();
+        }
+
+        List<Integer> groupCountList = new ArrayList<>();
+        int count = 1;
+
+        for (int i = 1; i < exportDataList.size(); i++) {
+            if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
+                count++;
+            } else {
+                groupCountList.add(count);
+                count = 1;
+            }
+        }
+        // 处理完最后一条后
+        groupCountList.add(count);
+        return groupCountList;
+    }
+}

+ 50 - 0
snowy-common/src/main/java/vip/xiaonuo/common/exception/CommonException.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.exception;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 通用异常
+ *
+ * @author xuyuxiang
+ * @date 2020/4/8 15:54
+ */
+@Getter
+@Setter
+public class CommonException extends RuntimeException {
+
+    private Integer code;
+
+    private String msg;
+
+    public CommonException() {
+        super("服务器异常");
+        this.code = 500;
+        this.msg = "服务器异常";
+    }
+
+    public CommonException(String msg, Object... arguments) {
+        super(StrUtil.format(msg, arguments));
+        this.code = 500;
+        this.msg = StrUtil.format(msg, arguments);
+    }
+
+    public CommonException(Integer code, String msg, Object... arguments) {
+        super(StrUtil.format(msg, arguments));
+        this.code = code;
+        this.msg = StrUtil.format(msg, arguments);
+    }
+}

+ 61 - 0
snowy-common/src/main/java/vip/xiaonuo/common/handler/CommonSm4CbcTypeHandler.java

@@ -0,0 +1,61 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.handler;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import vip.xiaonuo.common.util.CommonCryptogramUtil;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * Sm4Cbc加解密
+ *
+ * @author wanglei
+ * @date 2022/9/30 15:24
+ **/
+@MappedJdbcTypes(JdbcType.VARCHAR)
+public class CommonSm4CbcTypeHandler<T> extends BaseTypeHandler<T> {
+
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
+        ps.setString(i, CommonCryptogramUtil.doSm4CbcEncrypt((String)parameter));
+    }
+
+    @SuppressWarnings("ALL")
+    @Override
+    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        String columnValue = rs.getString(columnName);
+        //有一些可能是空字符
+        return StringUtils.isBlank(columnValue) ? (T)columnValue : (T)CommonCryptogramUtil.doSm4CbcDecrypt(columnValue);
+    }
+
+    @SuppressWarnings("ALL")
+    @Override
+    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        String columnValue = rs.getString(columnIndex);
+        return StringUtils.isBlank(columnValue) ? (T)columnValue : (T)CommonCryptogramUtil.doSm4CbcDecrypt(columnValue);
+    }
+
+    @SuppressWarnings("ALL")
+    @Override
+    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        String columnValue = cs.getString(columnIndex);
+        return StringUtils.isBlank(columnValue) ? (T)columnValue : (T)CommonCryptogramUtil.doSm4CbcDecrypt(columnValue);
+    }
+}

+ 175 - 0
snowy-common/src/main/java/vip/xiaonuo/common/listener/CommonDataChangeEventCenter.java

@@ -0,0 +1,175 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.listener;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import vip.xiaonuo.common.exception.CommonException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 通用数据变化事件中心 事件发布器
+ *
+ * @author xuyuxiang
+ * @date 2023/3/3 10:14
+ **/
+public class CommonDataChangeEventCenter {
+
+    // --------- 注册侦听器 
+
+    private static List<CommonDataChangeListener> listenerList = new ArrayList<>();
+
+    /**
+     * 获取已注册的所有侦听器
+     * @return / 
+     */
+    public static List<CommonDataChangeListener> getListenerList() {
+        return listenerList;
+    }
+
+    /**
+     * 重置侦听器集合
+     * @param listenerList / 
+     */
+    public static void setListenerList(List<CommonDataChangeListener> listenerList) {
+        if(listenerList == null) {
+            throw new CommonException("重置的侦听器集合不可以为空");
+        }
+        CommonDataChangeEventCenter.listenerList = listenerList;
+    }
+
+    /**
+     * 注册一个侦听器 
+     * @param listener / 
+     */
+    public static void registerListener(CommonDataChangeListener listener) {
+        if(listener == null) {
+            throw new CommonException("注册的侦听器不可以为空");
+        }
+        listenerList.add(listener);
+    }
+
+    /**
+     * 注册一组侦听器 
+     * @param listenerList / 
+     */
+    public static void registerListenerList(List<CommonDataChangeListener> listenerList) {
+        if(listenerList == null) {
+            throw new CommonException("注册的侦听器不可以为空");
+        }
+        for (CommonDataChangeListener listener : listenerList) {
+            if(listener == null) {
+                throw new CommonException("注册的侦听器不可以为空");
+            }
+        }
+        CommonDataChangeEventCenter.listenerList.addAll(listenerList);
+    }
+
+    /**
+     * 移除一个侦听器 
+     * @param listener / 
+     */
+    public static void removeListener(CommonDataChangeListener listener) {
+        listenerList.remove(listener);
+    }
+
+    /**
+     * 移除指定类型的所有侦听器 
+     * @param cls / 
+     */
+    public static void removeListener(Class<? extends CommonDataChangeListener> cls) {
+        ArrayList<CommonDataChangeListener> listenerListCopy = new ArrayList<>(listenerList);
+        for (CommonDataChangeListener listener : listenerListCopy) {
+            if(cls.isAssignableFrom(listener.getClass())) {
+                listenerList.remove(listener);
+            }
+        }
+    }
+
+    /**
+     * 清空所有已注册的侦听器 
+     */
+    public static void clearListener() {
+        listenerList.clear();
+    }
+
+    /**
+     * 判断是否已经注册了指定侦听器 
+     * @param listener / 
+     * @return / 
+     */
+    public static boolean hasListener(CommonDataChangeListener listener) {
+        return listenerList.contains(listener);
+    }
+
+    /**
+     * 判断是否已经注册了指定类型的侦听器 
+     * @param cls / 
+     * @return / 
+     */
+    public static boolean hasListener(Class<? extends CommonDataChangeListener> cls) {
+        for (CommonDataChangeListener listener : listenerList) {
+            if(cls.isAssignableFrom(listener.getClass())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // --------- 事件发布-添加 --------- //
+
+    /**
+     * 执行添加事件发布,数据集合
+     *
+     * @author xuyuxiang
+     * @date 2023/3/3 10:22
+     **/
+    public static void doAddWithData(String dataType, JSONArray jsonArray) {
+        for (CommonDataChangeListener listener : listenerList) {
+            listener.doAddWithDataIdList(dataType, jsonArray.stream().map(o -> JSONUtil.parseObj(o).getStr("id")).collect(Collectors.toList()));
+            listener.doAddWithDataList(dataType, jsonArray);
+        }
+    }
+
+    // --------- 事件发布-更新 --------- //
+
+    /**
+     * 执行更新事件发布,数据集合
+     *
+     * @author xuyuxiang
+     * @date 2023/3/3 10:22
+     **/
+    public static void doUpdateWithData(String dataType, JSONArray jsonArray) {
+        for (CommonDataChangeListener listener : listenerList) {
+            listener.doUpdateWithDataIdList(dataType, jsonArray.stream().map(o -> JSONUtil.parseObj(o).getStr("id")).collect(Collectors.toList()));
+            listener.doUpdateWithDataList(dataType, jsonArray);
+        }
+    }
+
+    // --------- 事件发布-删除 --------- //
+
+    /**
+     * 执行删除事件发布,ID集合
+     *
+     * @author xuyuxiang
+     * @date 2023/3/3 10:22
+     **/
+    public static void doDeleteWithDataId(String dataType, List<String> dataIdList) {
+        for (CommonDataChangeListener listener : listenerList) {
+            listener.doDeleteWithDataIdList(dataType, dataIdList);
+        }
+    }
+}

+ 76 - 0
snowy-common/src/main/java/vip/xiaonuo/common/listener/CommonDataChangeListener.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.listener;
+
+import cn.hutool.json.JSONArray;
+
+import java.util.List;
+
+/**
+ * 通用数据变化侦听器,你可以实现该侦听器接口,在数据新增、更新、删除时进行一些AOP操作
+ *
+ * @author xuyuxiang
+ * @date 2023/3/3 10:14
+ **/
+public interface CommonDataChangeListener {
+
+    /**
+     * 执行添加,ID集合
+     *
+     * @param dataType 数据类型,如USER、ORG,自行定义
+     * @param dataIdList 被添加的数据ID集合
+     * @author xuyuxiang
+     * @date 2023/3/3 10:24
+     **/
+    void doAddWithDataIdList(String dataType, List<String> dataIdList);
+
+    /**
+     * 执行添加,数据集合
+     *
+     * @param dataType 数据类型,如USER、ORG,自行定义
+     * @param jsonArray 被添加的数据集合
+     * @author xuyuxiang
+     * @date 2023/3/3 10:24
+     **/
+    void doAddWithDataList(String dataType, JSONArray jsonArray);
+
+    /**
+     * 执行更新,ID集合
+     *
+     * @param dataType 数据类型,如USER、ORG,自行定义
+     * @param dataIdList 被更新的数据ID集合
+     * @author xuyuxiang
+     * @date 2023/3/3 10:24
+     **/
+    void doUpdateWithDataIdList(String dataType, List<String> dataIdList);
+
+    /**
+     * 执行更新,数据集合
+     *
+     * @param dataType 数据类型,如USER、ORG,自行定义
+     * @param jsonArray 被更新的数据集合
+     * @author xuyuxiang
+     * @date 2023/3/3 10:24
+     **/
+    void doUpdateWithDataList(String dataType, JSONArray jsonArray);
+
+    /**
+     * 执行删除,ID集合
+     *
+     * @param dataType 数据类型,如USER、ORG,自行定义
+     * @param dataIdList 被删除的数据ID集合
+     * @author xuyuxiang
+     * @date 2023/3/3 10:24
+     **/
+    void doDeleteWithDataIdList(String dataType, List<String> dataIdList);
+}

+ 79 - 0
snowy-common/src/main/java/vip/xiaonuo/common/page/CommonPageRequest.java

@@ -0,0 +1,79 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.page;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+import vip.xiaonuo.common.util.CommonServletUtil;
+
+import java.util.List;
+
+/**
+ * 通用分页请求
+ *
+ * @author xuyuxiang
+ * @date 2021/12/18 14:43
+ */
+@Slf4j
+public class CommonPageRequest {
+
+    private static final String PAGE_SIZE_PARAM_NAME = "size";
+
+    private static final String PAGE_PARAM_NAME = "current";
+
+    private static final Integer PAGE_SIZE_MAX_VALUE = 100;
+
+    public static <T> Page<T> defaultPage() {
+        return defaultPage(null);
+    }
+
+    public static <T> Page<T> defaultPage(List<OrderItem> orderItemList) {
+
+        int size = 20;
+
+        int page = 1;
+
+        //每页条数
+        String pageSizeString = CommonServletUtil.getParamFromRequest(PAGE_SIZE_PARAM_NAME);
+        if (ObjectUtil.isNotEmpty(pageSizeString)) {
+            try {
+                size = Convert.toInt(pageSizeString);
+                if(size > PAGE_SIZE_MAX_VALUE) {
+                    size = PAGE_SIZE_MAX_VALUE;
+                }
+            } catch (Exception e) {
+                log.error(">>> 分页条数转换异常:", e);
+                size = 20;
+            }
+        }
+
+        //第几页
+        String pageString = CommonServletUtil.getParamFromRequest(PAGE_PARAM_NAME);
+        if (ObjectUtil.isNotEmpty(pageString)) {
+            try {
+                page = Convert.toInt(pageString);
+            } catch (Exception e) {
+                log.error(">>> 分页页数转换异常:", e);
+                page = 1;
+            }
+        }
+        Page<T> objectPage = new Page<>(page, size);
+        if (ObjectUtil.isNotEmpty(orderItemList)) {
+            objectPage.setOrders(orderItemList);
+        }
+        return objectPage;
+    }
+}

+ 63 - 0
snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonEntity.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>
+ *     通用基础字段实体:创建时间、创建人、修改时间、修改人,需要此通用字段的实体可继承此类,
+ *     继承此类要求数据表有对应的字段
+ * </p>
+ *
+ * @author xuyuxiang
+ * @date 2020/3/10 16:02
+ */
+@Getter
+@Setter
+public class CommonEntity implements Serializable {
+
+    /** 删除标志 */
+    @TableLogic
+    @ApiModelProperty(value = "删除标志", position = 999)
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** 创建时间 */
+    @ApiModelProperty(value = "创建时间", position = 1000)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 创建人 */
+    @ApiModelProperty(value = "创建人", position = 1001)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** 更新时间 */
+    @ApiModelProperty(value = "更新时间", position = 1002)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** 更新人 */
+    @ApiModelProperty(value = "更新人", position = 1003)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+}

+ 160 - 0
snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonResult.java

@@ -0,0 +1,160 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.pojo;
+
+import io.swagger.annotations.ApiModelProperty;
+import springfox.documentation.builders.ResponseMessageBuilder;
+import springfox.documentation.service.ResponseMessage;
+import vip.xiaonuo.common.enums.CommonExceptionEnum;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 对Ajax请求返回Json格式数据的简易封装
+ *
+ * @author xuyuxiang
+ * @date 2022/8/15 16:08
+ **/
+public class CommonResult<T> implements Serializable{
+    private static final long serialVersionUID = 1L;
+    public static final int CODE_SUCCESS = 200;
+    public static final int CODE_ERROR = 500;
+
+    @ApiModelProperty(value = "状态码")
+    private int code;
+
+    @ApiModelProperty(value = "提示语")
+    private String msg;
+
+    @ApiModelProperty(value = "返回数据")
+    private T data;
+
+    public CommonResult() {
+    }
+
+    public CommonResult(int code, String msg, T data) {
+        this.setCode(code);
+        this.setMsg(msg);
+        this.setData(data);
+    }
+
+    /**
+     * 获取code
+     * @return code
+     */
+    public Integer getCode() {
+        return this.code;
+    }
+
+    /**
+     * 获取msg
+     * @return msg
+     */
+    public String getMsg() {
+        return this.msg;
+    }
+    /**
+     * 获取data
+     * @return data
+     */
+    public T getData() {
+        return this.data;
+    }
+
+    /**
+     * 给code赋值,连缀风格
+     * @param code code
+     * @return 对象自身
+     */
+    public CommonResult<T> setCode(int code) {
+        this.code = code;
+        return this;
+    }
+
+    /**
+     * 给msg赋值,连缀风格
+     * @param msg msg
+     * @return 对象自身
+     */
+    public CommonResult<T> setMsg(String msg) {
+        this.msg = msg;
+        return this;
+    }
+
+    /**
+     * 给data赋值,连缀风格
+     * @param data data
+     * @return 对象自身
+     */
+    public CommonResult<T> setData(T data) {
+        this.data = data;
+        return this;
+    }
+
+
+    // ============================  构建  ==================================
+
+    // 构建成功
+    public static <T> CommonResult<T> ok() {
+        return new CommonResult<>(CODE_SUCCESS, "操作成功", null);
+    }
+    public static <T> CommonResult<T> ok(String msg) {
+        return new CommonResult<>(CODE_SUCCESS, msg, null);
+    }
+    public static <T> CommonResult<T> code(int code) {
+        return new CommonResult<>(code, null, null);
+    }
+    public static <T> CommonResult<T> data(T data) {
+        return new CommonResult<>(CODE_SUCCESS, "操作成功", data);
+    }
+
+    // 构建失败
+    public static <T> CommonResult<T> error() {
+        return new CommonResult<>(CODE_ERROR, "抱歉,系统遇到了问题,请稍后再试或联系技术支持", null);
+    }
+    public static <T> CommonResult<T> error(String msg) {
+        return new CommonResult<>(CODE_ERROR, msg, null);
+    }
+
+    // 构建指定状态码
+    public static <T> CommonResult<T> get(int code, String msg, T data) {
+        return new CommonResult<>(code, msg, data);
+    }
+
+    /*
+     * toString()
+     */
+    @Override
+    public String toString() {
+        return "{"
+                + "\"code\": " + this.getCode()
+                + ", \"msg\": \"" + this.getMsg() + "\""
+                + ", \"data\": \"" + this.getData() + "\""
+                + "}";
+    }
+
+    /**
+     * 响应状态码集合
+     *
+     * @author xuyuxiang
+     * @date 2022/7/25 13:36
+     **/
+    public static List<ResponseMessage> responseList() {
+        return Arrays.stream(CommonExceptionEnum.values()).map(commonExceptionEnum -> new ResponseMessageBuilder()
+                .code(commonExceptionEnum.getCode()).message(commonExceptionEnum.getMessage()).build())
+                .collect(Collectors.toList());
+    }
+}

+ 146 - 0
snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonValidList.java

@@ -0,0 +1,146 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.pojo;
+
+import lombok.Data;
+
+import javax.validation.Valid;
+import java.util.*;
+
+/**
+ * 可被校验的通用List
+ *
+ * @author xuyuxiang
+ * @date 2022/7/28 16:08
+ **/
+@Data
+public class CommonValidList<E> implements List<E> {
+
+    @Valid
+    private List<E> list = new LinkedList<>();
+
+    @Override
+    public int size() {
+        return list.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return list.isEmpty();
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return list.contains(o);
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return list.iterator();
+    }
+
+    @Override
+    public Object[] toArray() {
+        return list.toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return list.toArray(a);
+    }
+
+    @Override
+    public boolean add(E e) {
+        return list.add(e);
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        return list.remove(o);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        return list.containsAll(c);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
+        return list.addAll(c);
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends E> c) {
+        return list.addAll(index, c);
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        return list.removeAll(c);
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        return list.retainAll(c);
+    }
+
+    @Override
+    public void clear() {
+        list.clear();
+    }
+
+    @Override
+    public E get(int index) {
+        return list.get(index);
+    }
+
+    @Override
+    public E set(int index, E element) {
+        return list.set(index, element);
+    }
+
+    @Override
+    public void add(int index, E element) {
+        list.add(index, element);
+    }
+
+    @Override
+    public E remove(int index) {
+        return list.remove(index);
+    }
+
+    @Override
+    public int indexOf(Object o) {
+        return list.indexOf(o);
+    }
+
+    @Override
+    public int lastIndexOf(Object o) {
+        return list.lastIndexOf(o);
+    }
+
+    @Override
+    public ListIterator<E> listIterator() {
+        return list.listIterator();
+    }
+
+    @Override
+    public ListIterator<E> listIterator(int index) {
+        return list.listIterator(index);
+    }
+
+    @Override
+    public List<E> subList(int fromIndex, int toIndex) {
+        return list.subList(fromIndex, toIndex);
+    }
+}

+ 32 - 0
snowy-common/src/main/java/vip/xiaonuo/common/pojo/CommonWrapperInterface.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.pojo;
+
+import cn.hutool.json.JSONObject;
+
+/**
+ * 通用包装接口
+ *
+ * @author xuyuxiang
+ * @date 2022/9/15 21:17
+ */
+public interface CommonWrapperInterface<T> {
+
+    /**
+     * 执行包装
+     *
+     * @author xuyuxiang
+     * @date 2022/9/15 21:17
+     */
+    JSONObject doWrap(T wrapperObject);
+}

+ 37 - 0
snowy-common/src/main/java/vip/xiaonuo/common/prop/CommonProperties.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.prop;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 通用基础配置
+ *
+ * @author xuyuxiang
+ * @date 2022/1/2 17:03
+ */
+@Getter
+@Setter
+@Component
+@ConfigurationProperties(prefix = "snowy.config.common")
+public class CommonProperties {
+
+    /** 前端地址 */
+    private String frontUrl;
+
+    /** 后端地址 */
+    private String backendUrl;
+}

+ 31 - 0
snowy-common/src/main/java/vip/xiaonuo/common/sse/CommonSseParam.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.sse;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 通用SSE参数
+ *
+ * @author diantu
+ * @date 2023/7/10
+ */
+@Getter
+@Setter
+public class CommonSseParam {
+
+    private String clientId;
+
+    private String loginId;
+}

+ 30 - 0
snowy-common/src/main/java/vip/xiaonuo/common/timer/CommonTimerTaskRunner.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.timer;
+
+/**
+ * 定时器执行者,定时器都要实现本接口,并需要把实现类加入到spring容器中
+ *
+ * @author xuyuxiang
+ * @date 2022/8/15 16:09
+ **/
+public interface CommonTimerTaskRunner {
+
+    /**
+     * 任务执行的具体内容
+     *
+     * @author xuyuxiang
+     * @date 2022/8/15 16:09
+     **/
+    void action();
+}

+ 126 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonAvatarUtil.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 通用头像工具类,生成文字头像
+ *
+ * @author xuyuxiang
+ * @date 2022/7/5 17:36
+ **/
+public class CommonAvatarUtil {
+
+    /**
+     * 绘制字体头像,如果是英文名,只显示首字母大写,
+     * 如果是中文名,只显示最后两个字
+     * 返回图片base64
+     *
+     * @author xuyuxiang
+     * @date 2022/7/5 17:36
+     **/
+    public static String generateImg(String name) {
+        int width = 100;
+        int height = 100;
+        int nameLength = name.length();
+        String nameWritten;
+        // 如果用户输入的姓名少于等于2个字符,不用截取
+        if (nameLength <= 2) {
+            nameWritten = name;
+        } else {
+            // 如果用户输入的姓名大于等于3个字符,截取后面两位
+            String first = StrUtil.sub(name, 0, 1);
+            if (isChinese(first)) {
+                // 截取倒数两位汉字
+                nameWritten = name.substring(nameLength - 2);
+            } else {
+                // 截取前面的两个英文字母
+                nameWritten = StrUtil.sub(name, 0, 1).toUpperCase();
+            }
+        }
+        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2 = (Graphics2D) bufferedImage.getGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        g2.setBackground(getRandomColor());
+        g2.clearRect(0, 0, width, height);
+        g2.setPaint(Color.WHITE);
+        Font font;
+        // 两个字及以上
+        if(nameWritten.length() >= 2) {
+            font = new Font("微软雅黑", Font.BOLD, 30);
+            g2.setFont(font);
+            String firstWritten = StrUtil.sub(nameWritten, 0, 1);
+            String secondWritten = StrUtil.sub(nameWritten, 0, 2);
+            // 两个中文 如 言曌
+            if (isChinese(firstWritten) && isChinese(secondWritten)) {
+                g2.drawString(nameWritten, 20, 60);
+            }
+            // 首中次英 如 罗Q
+            else if (isChinese(firstWritten) && !isChinese(secondWritten)) {
+                g2.drawString(nameWritten, 27, 60);
+                // 首英 如 AB
+            } else {
+                nameWritten = nameWritten.substring(0,1);
+            }
+        }
+        // 一个字
+        if(nameWritten.length() == 1) {
+            // 中文
+            if(isChinese(nameWritten)) {
+                font = new Font("微软雅黑", Font.PLAIN, 50);
+                g2.setFont(font);
+                g2.drawString(nameWritten, 25, 70);
+            } else {
+                font = new Font("微软雅黑", Font.PLAIN, 55);
+                g2.setFont(font);
+                g2.drawString(nameWritten.toUpperCase(), 33, 67);
+            }
+        }
+        return ImgUtil.toBase64DataUri(bufferedImage, "jpg");
+    }
+
+    /**
+     * 获得随机颜色
+     *
+     * @author xuyuxiang
+     * @date 2022/7/5 17:41
+     **/
+    private static Color getRandomColor() {
+        String[] beautifulColors =
+                new String[]{"114,101,230", "255,191,0", "0,162,174", "245,106,0", "24,144,255", "96,109,128"};
+        String[] color = beautifulColors[RandomUtil.randomInt(beautifulColors.length)].split(StrUtil.COMMA);
+        return new Color(Integer.parseInt(color[0]), Integer.parseInt(color[1]),
+                Integer.parseInt(color[2]));
+    }
+
+    /**
+     * 判断字符串是否为中文
+     *
+     * @author xuyuxiang
+     * @date 2022/7/5 17:41
+     **/
+    private static boolean isChinese(String str) {
+        String regEx = "[\\u4e00-\\u9fa5]+";
+        Pattern p = Pattern.compile(regEx);
+        Matcher m = p.matcher(str);
+        return m.find();
+    }
+}

+ 142 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonCryptogramUtil.java

@@ -0,0 +1,142 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import com.antherd.smcrypto.sm2.Sm2;
+import com.antherd.smcrypto.sm3.Sm3;
+import com.antherd.smcrypto.sm4.Sm4;
+import com.antherd.smcrypto.sm4.Sm4Options;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 加密工具类,本框架目前使用 https://github.com/antherd/sm-crypto 项目中一些加解密方式
+ * 使用小伙伴需要过等保密评相关,请在此处更改为自己的加密方法,或加密机,使用加密机同时需要替换公钥,私钥在内部无法导出,提供加密的方法
+ * 如果不涉及到加密机方面的内容,请更改公私要为自己重新生成的,生成方式请看集成的sm-crypto主页
+ *
+ * @author yubaoshan
+ * @date 2022/9/15 21:51
+ */
+@Slf4j
+public class CommonCryptogramUtil {
+
+    /** 公钥 */
+    private static final String PUBLIC_KEY = "04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54";
+
+    /** 私钥 */
+    private static final String PRIVATE_KEY = "3037723d47292171677ec8bd7dc9af696c7472bc5f251b2cec07e65fdef22e25";
+
+    /** SM4的对称秘钥(生产环境需要改成自己使用的) 16 进制字符串,要求为 128 比特 */
+    private static final String KEY = "0123456789abcdeffedcba9876543210";
+
+    /**
+     * 加密方法(Sm2 的专门针对前后端分离,非对称秘钥对的方式,暴露出去的公钥,对传输过程中的密码加个密)
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 待加密数据
+     * @return 加密后的密文
+     */
+    public static String doSm2Encrypt(String str) {
+        return Sm2.doEncrypt(str, PUBLIC_KEY);
+    }
+
+    /**
+     * 解密方法
+     * 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 密文
+     * @return 解密后的明文
+     */
+    public static String doSm2Decrypt(String str) {
+        // 解密
+        return Sm2.doDecrypt(str, PRIVATE_KEY);
+    }
+
+    /**
+     * 加密方法
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 待加密数据
+     * @return 加密后的密文
+     */
+    public static String doSm4CbcEncrypt(String str) {
+        // SM4 加密  cbc模式
+        Sm4Options sm4Options4 = new Sm4Options();
+        sm4Options4.setMode("cbc");
+        sm4Options4.setIv("fedcba98765432100123456789abcdef");
+        return Sm4.encrypt(str, KEY, sm4Options4);
+    }
+
+    /**
+     * 解密方法
+     * 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 密文
+     * @return 解密后的明文
+     */
+    public static String doSm4CbcDecrypt(String str) {
+        // 解密,cbc 模式,输出 utf8 字符串
+        Sm4Options sm4Options8 = new Sm4Options();
+        sm4Options8.setMode("cbc");
+        sm4Options8.setIv("fedcba98765432100123456789abcdef");
+        String docString =  Sm4.decrypt(str, KEY, sm4Options8);
+        if ("".equals(docString)) {
+            log.warn(">>> 字段解密失败,返回原文值:{}", str);
+            return str;
+        } else {
+            return docString;
+        }
+    }
+
+    /**
+     * 纯签名
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 待签名数据
+     * @return 签名结果
+     */
+    public static String doSignature(String str) {
+        return Sm2.doSignature(str, PRIVATE_KEY);
+    }
+
+    /**
+     * 验证签名结果
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param originalStr 签名原文数据
+     * @param str 签名结果
+     * @return 是否通过
+     */
+    public static boolean doVerifySignature(String originalStr, String str) {
+        return Sm2.doVerifySignature(originalStr, str, PUBLIC_KEY);
+    }
+
+    /**
+     * 通过杂凑算法取得hash值,用于做数据完整性保护
+     *
+     * @author yubaoshan
+     * @date 2022/9/15 21:51
+     * @param str 字符串
+     * @return hash 值
+     */
+    public static String doHashValue(String str) {
+        return Sm3.sm3(str);
+    }
+}

+ 63 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonDownloadUtil.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.URLUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * 文件下载工具类,使用本类前,对参数校验的异常使用CommonResponseUtil.renderError()方法进行渲染
+ *
+ * @author xuyuxiang
+ * @date 2020/8/5 21:45
+ */
+@Slf4j
+public class CommonDownloadUtil {
+
+    /**
+     * 下载文件
+     *
+     * @param file     要下载的文件
+     * @param response 响应
+     * @author xuyuxiang
+     * @date 2020/8/5 21:46
+     */
+    public static void download(File file, HttpServletResponse response) {
+        download(file.getName(), FileUtil.readBytes(file), response);
+    }
+
+    /**
+     * 下载文件
+     *
+     * @author xuyuxiang
+     * @date 2022/7/31 10:57
+     */
+    public static void download(String fileName, byte[] fileBytes, HttpServletResponse response) {
+        try {
+            response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(fileName));
+            response.addHeader("Content-Length", "" + fileBytes.length);
+            response.setHeader("Access-Control-Allow-Origin", "*");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setContentType("application/octet-stream;charset=UTF-8");
+            IoUtil.write(response.getOutputStream(), true, fileBytes);
+        } catch (IOException e) {
+            log.error(">>> 文件下载异常:", e);
+        }
+    }
+}

+ 50 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonEmailUtil.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.StrUtil;
+import vip.xiaonuo.common.exception.CommonException;
+
+/**
+ * 通用邮件工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/8/25 15:10
+ **/
+public class CommonEmailUtil {
+
+    /**
+     * 判断是否邮箱
+     *
+     * @author xuyuxiang
+     * @date 2022/8/15 13:32
+     **/
+    public static boolean isEmail(String email) {
+        return  Validator.isEmail(email);
+    }
+
+    /**
+     * 校验邮箱格式
+     *
+     * @author xuyuxiang
+     * @date 2022/8/15 13:32
+     **/
+    public static void validEmail(String emails) {
+        StrUtil.split(emails, StrUtil.COMMA).forEach(email -> {
+            if(!isEmail(email)) {
+                throw new CommonException("邮件地址:{}格式错误", email);
+            }
+        });
+    }
+}

+ 40 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonFilterExceptionUtil.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * 过滤器异常工具类,用于处理过滤器中的异常
+ * 原理:将异常转发到/errorView进行处理
+ *
+ * @author xuyuxiang
+ * @date 2022/7/18 18:59
+ **/
+public class CommonFilterExceptionUtil {
+
+    /**
+     * 处理过滤器中的异常
+     *
+     * @author xuyuxiang
+     * @date 2022/7/18 19:00
+     **/
+    public static void handleFilterException(ServletRequest request, ServletResponse response, Exception e) {
+        try {
+            request.setAttribute("model", e);
+            request.getRequestDispatcher("/errorView").forward(request, response);
+        } catch (Exception ignored) {
+        }
+    }
+}

+ 108 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonIpAddressUtil.java

@@ -0,0 +1,108 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.net.Ipv4Util;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.lionsoul.ip2region.xdb.Searcher;
+import vip.xiaonuo.common.exception.CommonException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * 根据ip地址定位工具类,离线方式
+ * 参考地址:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
+ *
+ * @author xuyuxiang
+ * @date 2020/3/16 11:25
+ */
+@Slf4j
+public class CommonIpAddressUtil {
+
+    private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";
+
+    private static final Searcher searcher;
+
+    static {
+        String fileName = "/ip2region.xdb";
+        File existFile = FileUtil.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
+        if(!FileUtil.exist(existFile)) {
+            InputStream resourceAsStream = CommonIpAddressUtil.class.getResourceAsStream(fileName);
+            if(ObjectUtil.isEmpty(resourceAsStream)) {
+                throw new CommonException("CommonIpAddressUtil初始化失败,原因:IP地址库数据不存在");
+            }
+            FileUtil.writeFromStream(resourceAsStream, existFile);
+        }
+
+        String dbPath = existFile.getPath();
+
+        // 1、从 dbPath 加载整个 xdb 到内存。
+        byte[] cBuff;
+        try {
+            cBuff = Searcher.loadContentFromFile(dbPath);
+        } catch (Exception e) {
+            log.error(">>> CommonIpAddressUtil初始化异常:", e);
+            throw new CommonException("CommonIpAddressUtil初始化异常");
+        }
+
+        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
+        try {
+            searcher = Searcher.newWithBuffer(cBuff);
+        } catch (Exception e) {
+            log.error(">>> CommonIpAddressUtil初始化异常:", e);
+            throw new CommonException("CommonIpAddressUtil初始化异常");
+        }
+    }
+
+    /**
+     * 获取客户端ip
+     *
+     * @author xuyuxiang
+     * @date 2020/3/19 9:32
+     */
+    public static String getIp(HttpServletRequest request) {
+        if (ObjectUtil.isEmpty(request)) {
+            return Ipv4Util.LOCAL_IP;
+        } else {
+            try {
+                String remoteHost = ServletUtil.getClientIP(request);
+                return LOCAL_REMOTE_HOST.equals(remoteHost) ? Ipv4Util.LOCAL_IP : remoteHost;
+            } catch (Exception e) {
+                log.error(">>> 获取客户端ip异常:", e);
+                return Ipv4Util.LOCAL_IP;
+            }
+        }
+    }
+
+    /**
+     * 根据IP地址离线获取城市
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 23:14
+     */
+    public static String getCityInfo(String ip) {
+        try {
+            ip = ip.trim();
+            // 3、执行查询
+            String region = searcher.searchByStr(ip);
+            return region.replace("0|", "").replace("|0", "");
+        } catch (Exception e) {
+            return "未知";
+        }
+    }
+}

+ 72 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonJoinPointUtil.java

@@ -0,0 +1,72 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+/**
+ * Spring切面工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/9/2 15:51
+ */
+public class CommonJoinPointUtil {
+
+    /**
+     * 获取切面的参数JSON
+     *
+     * @author xuyuxiang
+     * @date 2022/9/2 15:51
+     */
+    public static String getArgsJsonString(JoinPoint joinPoint) {
+        Signature signature = joinPoint.getSignature();
+        // 参数名数组
+        String[] parameterNames = ((MethodSignature) signature).getParameterNames();
+        // 构造参数组集合
+        Map<String, Object> map = MapUtil.newHashMap();
+        Object[] args = joinPoint.getArgs();
+        for (int i = 0; i < args.length; i++) {
+            if(ObjectUtil.isNotEmpty(args[i]) && isUsefulParam(args[i])) {
+                if(JSONUtil.isTypeJSON(StrUtil.toString(args[i]))) {
+                    map.put(parameterNames[i], JSONUtil.parseObj(args[i]));
+                } else {
+                    map.put(parameterNames[i], JSONUtil.toJsonStr(args[i]));
+                }
+            }
+        }
+        return JSONUtil.toJsonStr(map);
+    }
+
+    /**
+     * 判断是否需要拼接的参数,过滤掉HttpServletRequest,MultipartFile,HttpServletResponse等类型参数
+     *
+     * @author xuyuxiang
+     * @date 2022/9/2 15:51
+     */
+    private static boolean isUsefulParam(Object arg) {
+        return !(arg instanceof MultipartFile) &&
+                !(arg instanceof HttpServletRequest) &&
+                !(arg instanceof HttpServletResponse);
+    }
+}

+ 125 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonNetWorkInfoUtil.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.system.SystemUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.*;
+
+/**
+ * 通用获取当前网速工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/9/1 23:45
+ */
+@Slf4j
+public class CommonNetWorkInfoUtil {
+
+    /**
+     * 网速测速时间2s
+     */
+    private static final int SLEEP_SECONDS = 2;
+
+    /**
+     * 获取网络上下行速率,格式{"UP": "123KB/S, "DOWN": "345KB/S"}
+     *
+     * @author xuyuxiang
+     * @date 2022/9/1 23:51
+     */
+    public static Map<String, String> getNetworkUpRate() {
+        Map<String, String> result = new HashMap<>();
+        Process pro = null;
+        Runtime r = Runtime.getRuntime();
+        BufferedReader input = null;
+        try {
+            boolean isWindows = SystemUtil.getOsInfo().isWindows();
+            String command = isWindows ? "netstat -e" : "ifconfig";
+            pro = r.exec(command);
+            input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
+            long[] result1 = readInLine(input, isWindows);
+            Thread.sleep(SLEEP_SECONDS * 1000);
+            pro.destroy();
+            input.close();
+            pro = r.exec(command);
+            input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
+            long[] result2 = readInLine(input, isWindows);
+            String upSpeed = FileUtil.readableFileSize(Convert.toLong(NumberUtil
+                    .div(NumberUtil.sub(result2[0], result1[0]), SLEEP_SECONDS)));
+            String downSpeed = FileUtil.readableFileSize(Convert.toLong(NumberUtil
+                    .div(NumberUtil.sub(result2[1], result1[1]), SLEEP_SECONDS)));
+            result.put("UP", upSpeed + (upSpeed.endsWith("B")?"/S":"B/S"));
+            result.put("DOWN", downSpeed + (downSpeed.endsWith("B")?"/S":"B/S"));
+        } catch (Exception e) {
+            log.info(">>> 网络测速失败:", e);
+        } finally {
+            if (input != null) {
+                try {
+                    input.close();
+                } catch (IOException e) {
+                    log.info(">>> 网络测速失败:", e);
+                }
+            }
+            Optional.ofNullable(pro).ifPresent(Process::destroy);
+        }
+        return result;
+    }
+
+    private static String formatNumber(double f) {
+        return new Formatter().format("%.2f", f).toString();
+    }
+
+    private static long[] readInLine(BufferedReader input, boolean isWindows) {
+        long[] arr = new long[2];
+        StringTokenizer tokenStat;
+        try {
+            if (isWindows) {
+                // 获取windows环境下的网口上下行速率
+                input.readLine();
+                input.readLine();
+                input.readLine();
+                input.readLine();
+                tokenStat = new StringTokenizer(input.readLine());
+                tokenStat.nextToken();
+                arr[0] = Long.parseLong(tokenStat.nextToken());
+                arr[1] = Long.parseLong(tokenStat.nextToken());
+            } else {
+                // 获取linux环境下的网口上下行速率
+                long rx = 0, tx = 0;
+                String line = null;
+                //RX packets:4171603 errors:0 dropped:0 overruns:0 frame:0
+                //TX packets:4171603 errors:0 dropped:0 overruns:0 carrier:0
+                while ((line = input.readLine()) != null) {
+                    if (line.contains("RX packets")) {
+                        rx += Long.parseLong(line.substring(line.indexOf("RX packets") + 11, line.indexOf(" ",
+                                line.indexOf("RX packets") + 11)));
+                    } else if (line.contains("TX packets")) {
+                        tx += Long.parseLong(line.substring(line.indexOf("TX packets") + 11, line.indexOf(" ",
+                                line.indexOf("TX packets") + 11)));
+                    }
+                }
+                arr[0] = rx;
+                arr[1] = tx;
+            }
+        } catch (Exception e) {
+            log.error(">>> 网络测速异常:", e);
+        }
+        return arr;
+    }
+}

+ 65 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonResponseUtil.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.ContentType;
+import cn.hutool.json.JSONUtil;
+import vip.xiaonuo.common.pojo.CommonResult;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 通用响应工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/8/4 9:40
+ **/
+public class CommonResponseUtil {
+
+    /**
+     * 以流的方式响应错误信息,默认错误消息
+     *
+     * @author xuyuxiang
+     * @date 2022/8/4 9:41
+     **/
+    public static void renderError(HttpServletResponse response) throws IOException {
+        renderError(response, null);
+    }
+
+    /**
+     * 以流的方式响应错误信息,指定错误消息
+     *
+     * @author xuyuxiang
+     * @date 2022/8/4 9:41
+     **/
+    public static void renderError(HttpServletResponse response, String msg) throws IOException {
+        response.setCharacterEncoding(CharsetUtil.UTF_8);
+        response.setContentType(ContentType.JSON.toString());
+        response.getWriter().write(JSONUtil.toJsonStr(ObjectUtil.isNotEmpty(msg)?CommonResult.error(msg):CommonResult.error()));
+    }
+
+    /**
+     * 以流的方式响应错误信息,指定错误码和错误消息
+     *
+     * @author xuyuxiang
+     * @date 2022/8/4 9:41
+     **/
+    public static void renderError(HttpServletResponse response, Integer code, String msg) throws IOException {
+        response.setCharacterEncoding(CharsetUtil.UTF_8);
+        response.setContentType(ContentType.JSON.toString());
+        response.getWriter().write(JSONUtil.toJsonStr(CommonResult.get(code, msg, null)));
+    }
+}

+ 99 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonServletUtil.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import vip.xiaonuo.common.exception.CommonException;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * HttpServlet工具类,获取当前request和response
+ *
+ * @author xuyuxiang
+ * @date 2020/3/30 15:09
+ */
+@Slf4j
+public class CommonServletUtil {
+
+    /**
+     * 从请求中中获取参数
+     *
+     * @author xuyuxiang
+     * @date 2021/10/14 10:44
+     **/
+    public static String getParamFromRequest(String paramName) {
+        HttpServletRequest request = getRequest();
+
+        // 1. 尝试从请求体里面读取
+        String paramValue = request.getParameter(paramName);
+
+        // 2. 尝试从header里读取
+        if (ObjectUtil.isEmpty(paramValue)) {
+            paramValue = request.getHeader(paramName);
+        }
+        // 3. 尝试从cookie里读取
+        if (ObjectUtil.isEmpty(paramValue)) {
+            Cookie[] cookies = request.getCookies();
+            if(ObjectUtil.isNotEmpty(cookies)) {
+                for (Cookie cookie : cookies) {
+                    String cookieName = cookie.getName();
+                    if (cookieName.equals(paramName)) {
+                        return cookie.getValue();
+                    }
+                }
+            }
+        }
+        // 4. 返回
+        return paramValue;
+    }
+
+    public static HttpServletRequest getRequest() {
+        ServletRequestAttributes servletRequestAttributes;
+        try {
+            servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        } catch (Exception e) {
+            log.error(">>> 非Web上下文无法获取Request:", e);
+            throw new CommonException("非Web上下文无法获取Request");
+        }
+        if (servletRequestAttributes == null) {
+            throw new CommonException("非Web上下文无法获取Request");
+        } else {
+            return servletRequestAttributes.getRequest();
+        }
+    }
+
+    public static HttpServletResponse getResponse() {
+        ServletRequestAttributes servletRequestAttributes;
+        try {
+            servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        } catch (Exception e) {
+            log.error(">>> 非Web上下文无法获取Response:", e);
+            throw new CommonException("非Web上下文无法获取Response");
+        }
+        if (servletRequestAttributes == null) {
+            throw new CommonException("非Web上下文无法获取Response");
+        } else {
+            return servletRequestAttributes.getResponse();
+        }
+    }
+
+    public static boolean isWeb() {
+        return RequestContextHolder.getRequestAttributes() != null;
+    }
+}

+ 183 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonTimeFormatUtil.java

@@ -0,0 +1,183 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+
+import java.util.Date;
+
+/**
+ * 时间格式化工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/6/24 15:28
+ **/
+public class CommonTimeFormatUtil {
+
+    private static final long ONE_MINUTE_SECONDS = 60;
+
+    private static final int BEFORE_DAWN_HOUR = 6;
+
+    private static final int MORNING_END_HOUR = 12;
+
+    private static final int NOON_END_HOUR = 13;
+
+    private static final int AFTERNOON_END_HOUR = 18;
+
+    private static final int NIGHT_END_HOUR = 24;
+
+    /**
+     * 将日期格式化为仿微信的日期
+     *
+     * @author xuyuxiang
+     * @date 2022/6/24 15:28
+     **/
+    public static String formatWxPastTime(Date date) {
+        if (DateUtil.between(date, DateUtil.date(), DateUnit.SECOND, false) < 0) {
+            //今天之后的时间显示年月日时分
+            return DateUtil.format(date, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
+        } else {
+            //如果是今年
+            if (DateUtil.thisYear() == DateUtil.year(date)) {
+                //如果是今天
+                if (DateUtil.isSameDay(date, DateUtil.date())) {
+                    //相差分钟数
+                    long betweenMinute = DateUtil.between(date, DateUtil.date(), DateUnit.MINUTE);
+                    //如果在1小时之内
+                    if (betweenMinute < ONE_MINUTE_SECONDS) {
+                        //一分钟之内,显示刚刚
+                        if (betweenMinute < 1) {
+                            return "刚刚";
+                        } else {
+                            //一分钟之外,显示xx分钟前
+                            return betweenMinute + "分钟前";
+                        }
+                    } else {
+                        //一小时之外,显示时分
+                        return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm");
+                    }
+                } else if (DateUtil.isSameDay(date, DateUtil.yesterday())) {
+                    //如果是昨天,显示昨天时分
+                    return "昨天 " + DateUtil.format(date, "HH:mm");
+                } else if (isThisWeek(date)) {
+                    //如果是本周
+                    String weekday;
+                    //获取是本周的第几天
+                    int dayOfWeek = DateUtil.dayOfWeek(date) - 1;
+                    switch (dayOfWeek) {
+                        case 1:
+                            weekday = "周一";
+                            break;
+                        case 2:
+                            weekday = "周二";
+                            break;
+                        case 3:
+                            weekday = "周三";
+                            break;
+                        case 4:
+                            weekday = "周四";
+                            break;
+                        case 5:
+                            weekday = "周五";
+                            break;
+                        case 6:
+                            weekday = "周六";
+                            break;
+                        default:
+                            weekday = "周日";
+                            break;
+                    }
+                    //显示本周时分
+                    return weekday + " " + DateUtil.format(date, "HH:mm");
+                } else {
+                    //否则显示月日时分
+                    return DateUtil.format(date, "MM-dd HH:mm");
+                }
+            } else {
+                //本年之外显示年月日时分
+                return DateUtil.format(date, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
+            }
+        }
+    }
+
+    /**
+     * 将秒数格式化为天时分秒
+     *
+     * @author xuyuxiang
+     * @date 2022/6/24 15:29
+     **/
+    public static String formatSeconds(long secondsParam) {
+        String result;
+        long days = secondsParam / ( ONE_MINUTE_SECONDS * ONE_MINUTE_SECONDS * NIGHT_END_HOUR);
+        long hours = (secondsParam % ( ONE_MINUTE_SECONDS * ONE_MINUTE_SECONDS * NIGHT_END_HOUR)) / (ONE_MINUTE_SECONDS * ONE_MINUTE_SECONDS);
+        long minutes = (secondsParam % ( ONE_MINUTE_SECONDS * ONE_MINUTE_SECONDS)) /ONE_MINUTE_SECONDS;
+        long seconds = secondsParam % ONE_MINUTE_SECONDS;
+        if(days > 0) {
+            result = days + "天" + hours + "小时" + minutes + "分钟" + seconds + "秒";
+        } else if(hours > 0) {
+            result = hours + "小时" + minutes + "分钟" + seconds + "秒";
+        } else if(minutes > 0) {
+            result = minutes + "分钟" + seconds + "秒";
+        } else {
+            result = seconds + "秒";
+        }
+        return result;
+    }
+
+    /**
+     * 判断日期是否是本周
+     *
+     * @param date 要判断的日期
+     * @return boolean
+     * @author xuyuxiang
+     * @date 2020/8/6 12:10
+     **/
+    private static boolean isThisWeek(Date date) {
+        //获取本周开始时间
+        DateTime beginOfWeek = DateUtil.beginOfWeek(DateUtil.date());
+        //获取与本周开始时间相差的天数
+        long betweenBegin = DateUtil.between(date, beginOfWeek, DateUnit.DAY, false) + 1;
+        //如果是同一天,或相差天数小于0,则是本周
+        return DateUtil.isSameDay(date, beginOfWeek) || betweenBegin < 0;
+    }
+
+    /**
+     * 根据今天日期获取早中晚
+     *
+     * @author xuyuxiang
+     * @date 2020/8/6 14:42
+     **/
+    private static String getTodayHour(Date date) {
+        String result = "";
+        int hour = DateUtil.hour(date, true);
+        if (hour >= 0 && hour <= BEFORE_DAWN_HOUR) {
+            result = "凌晨";
+        }
+        if (hour > BEFORE_DAWN_HOUR && hour < MORNING_END_HOUR) {
+            result = "上午";
+        }
+        if (hour == MORNING_END_HOUR) {
+            result = "中午";
+        }
+        if (hour >= NOON_END_HOUR && hour <= AFTERNOON_END_HOUR) {
+            result = "下午";
+        }
+        if (hour > AFTERNOON_END_HOUR && hour <= NIGHT_END_HOUR) {
+            result = "晚上";
+        }
+        return result;
+    }
+}

+ 80 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/CommonUaUtil.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.http.useragent.Browser;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 用户代理工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/9/2 15:34
+ */
+public class CommonUaUtil {
+
+    /**
+     * 获取客户端浏览器
+     *
+     * @author xuyuxiang
+     * @date 2020/3/19 14:53
+     */
+    public static String getBrowser(HttpServletRequest request) {
+        UserAgent userAgent = getUserAgent(request);
+        if (ObjectUtil.isEmpty(userAgent)) {
+            return StrUtil.DASHED;
+        } else {
+            String browser = userAgent.getBrowser().toString();
+            return "Unknown".equals(browser) ? StrUtil.DASHED : browser;
+        }
+    }
+
+    /**
+     * 获取客户端操作系统
+     *
+     * @author xuyuxiang
+     * @date 2022/9/2 15:36
+     */
+    public static String getOs(HttpServletRequest request) {
+        UserAgent userAgent = getUserAgent(request);
+        if (ObjectUtil.isEmpty(userAgent)) {
+            return StrUtil.DASHED;
+        } else {
+            String os = userAgent.getOs().toString();
+            return "Unknown".equals(os) ? StrUtil.DASHED : os;
+        }
+    }
+
+    /**
+     * 获取请求代理头
+     *
+     * @author xuyuxiang
+     * @date 2022/9/2 15:36
+     */
+    private static UserAgent getUserAgent(HttpServletRequest request) {
+        String userAgentStr = ServletUtil.getHeaderIgnoreCase(request, "User-Agent");
+        UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
+        if (ObjectUtil.isNotEmpty(userAgentStr)) {
+            if ("Unknown".equals(userAgent.getBrowser().getName())) {
+                userAgent.setBrowser(new Browser(userAgentStr, null, ""));
+            }
+        }
+        return userAgent;
+    }
+}

+ 184 - 0
snowy-common/src/main/java/vip/xiaonuo/common/util/UploadImage.java

@@ -0,0 +1,184 @@
+package vip.xiaonuo.common.util;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.UUID;
+import com.jcraft.jsch.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Date;
+
+@Component
+public class UploadImage {
+
+    @Value("${img.basePath}")
+    private   String basePath;
+
+    @Value("${img.ip}")
+    private    String ip ;
+//    private   String ip = "36.134.209.211";
+//    private   String ip = "192.168.0.13";
+
+    @Value("${img.user}")
+    private  String user ;
+
+//    private   String password = "!Hm537e@1";
+
+    @Value("${img.password}")
+    private   String password ;
+
+    @Value("${img.port}")
+    public   Integer port ;
+
+    @Value("${img.url}")
+    public   String url ;
+
+
+
+    /**
+     * 利用JSch包实现SFTP上传文件
+     * @param bytes  文件字节流
+     * @param fileName  文件名
+     * @throws Exception
+     */
+    public   void sshSftp(byte[] bytes,String path,String fileName) throws Exception{
+
+        // 服务器保存路径
+        String filepath = basePath +path ;
+        Session session = null;
+        Channel channel = null;
+
+        JSch jSch = new JSch();
+
+        if(port <=0){
+            //连接服务器,采用默认端口
+            session = jSch.getSession(user, ip);
+        }else{
+            //采用指定的端口连接服务器
+            session = jSch.getSession(user, ip ,port);
+        }
+
+        //如果服务器连接不上,则抛出异常
+        if (session == null) {
+            throw new Exception("session is null");
+        }
+
+        //设置登陆主机的密码
+        session.setPassword(password);//设置密码
+        //设置第一次登陆的时候提示,可选值:(ask | yes | no)
+        session.setConfig("userauth.gssapi-with-mic","no");
+        session.setConfig("StrictHostKeyChecking", "no");
+        //设置登陆超时时间
+        session.connect(30000);
+
+        OutputStream outstream = null;
+        try {
+            //创建sftp通信通道
+            channel = (Channel) session.openChannel("sftp");
+            channel.connect(1000);
+            ChannelSftp sftp = (ChannelSftp) channel;
+
+            //进入服务器指定的文件夹
+            sftp.cd(basePath);
+
+            SftpATTRS attrs = null;
+            try {
+                attrs = sftp.stat(filepath);
+            } catch (Exception e) {
+                // TODO: handle exception
+            }
+            if (attrs == null) {
+                sftp.mkdir(filepath);
+                System.out.println(("创建子目录:" + filepath));
+            }
+            sftp.cd(filepath);
+
+            //以下代码实现从本地上传一个文件到服务器,如果要实现下载,对换一下流就可以了
+            outstream = sftp.put(fileName);
+            outstream.write(bytes);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            //关流操作
+            if (outstream != null) {
+                outstream.flush();
+                outstream.close();
+            }
+            if (session != null) {
+                session.disconnect();
+            }
+            if (channel != null) {
+                channel.disconnect();
+            }
+            System.out.println("上传成功!");
+        }
+    }
+
+    //上传压缩的图片
+    public  String getImageCom(MultipartFile image) throws IOException {
+        //获取文件输入流
+        InputStream inputStream = image.getInputStream();
+        String originalFilename = image.getOriginalFilename();
+        String filenameExtension = StringUtils.getFilenameExtension(originalFilename);
+        String path = DateUtil.format(new Date(), "yyyy-MM");
+
+        try {
+            // 把图片读入到内存中
+            BufferedImage bufImg = ImageIO.read(inputStream);
+            // 压缩代码,存储图片文件byte数组
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            //防止图片变红,这一步非常重要
+            BufferedImage bufferedImage = new BufferedImage(bufImg.getWidth(), bufImg.getHeight(), BufferedImage.TYPE_INT_RGB);
+            bufferedImage.createGraphics().drawImage(bufImg,0,0, Color.WHITE,null);
+            //先转成jpg格式来压缩,然后在通过OSS来修改成源文件本来的后缀格式
+            ImageIO.write(bufferedImage,"jpg",bos);
+            byte[] bytes = bos.toByteArray();
+            String imgname = UUID.randomUUID() + "." + filenameExtension;
+            sshSftp(bytes, path, imgname);
+            return url+ path + "/" + imgname;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "上传失败";
+        } finally {
+            inputStream.close();
+        }
+    }
+
+
+    //上传原图
+    public  String uploadImg( MultipartFile image ) {
+        String originalFilename = image.getOriginalFilename();
+        String filenameExtension = StringUtils.getFilenameExtension(originalFilename);
+        String path = DateUtil.format(new Date(), "yyyy-MM");
+        try {
+            InputStream inputStream = image.getInputStream();
+            BufferedInputStream in = new BufferedInputStream(inputStream);
+            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
+
+            byte[] temp = new byte[1024];
+            int size = 0;
+            while ((size = in.read(temp)) != -1) {
+                out.write(temp, 0, size);
+            }
+            in.close();
+            byte[] content = out.toByteArray();
+            String imgname = UUID.randomUUID() + "." + filenameExtension;
+            sshSftp(content, path, imgname);
+            return url + path + "/" + imgname;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "上传失败";
+        }
+    }
+
+}
+
+
+

BIN
snowy-common/src/main/resources/ip2region.xdb


+ 15 - 0
snowy-plugin-api/README.md

@@ -0,0 +1,15 @@
+# 插件API接口模块
+
+####登录鉴权插件api接口: snowy-plugin-auth-api
+
+####业务功能插件api接口: snowy-plugin-biz-api
+
+####C端功能插件api接口: snowy-plugin-client-api
+
+####开发工具插件api接口: snowy-plugin-dev-api
+
+####代码生成api接口: snowy-plugin-gen-api
+
+####移动端管理api接口: snowy-plugin-mobile-api
+
+####系统功能插件api接口: snowy-plugin-sys-api

+ 38 - 0
snowy-plugin-api/pom.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>huimv.garden</groupId>
+        <artifactId>garden</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-plugin-api</artifactId>
+    <packaging>pom</packaging>
+    <description>插件API接口模块</description>
+
+    <modules>
+        <!-- 登录鉴权插件api接口 -->
+        <module>snowy-plugin-auth-api</module>
+
+        <!-- 业务功能插件api接口 -->
+        <module>snowy-plugin-biz-api</module>
+
+        <!-- C端功能插件api接口 -->
+        <module>snowy-plugin-client-api</module>
+
+        <!-- 开发工具插件api接口 -->
+        <module>snowy-plugin-dev-api</module>
+
+        <!-- 代码生成插件api接口 -->
+        <module>snowy-plugin-gen-api</module>
+
+        <!-- 移动端管理插件api接口 -->
+        <module>snowy-plugin-mobile-api</module>
+
+        <!-- 系统功能插件api接口 -->
+        <module>snowy-plugin-sys-api</module>
+    </modules>
+</project>

+ 1 - 0
snowy-plugin-api/snowy-plugin-auth-api/README.md

@@ -0,0 +1 @@
+# 登录鉴权插件api接口

+ 30 - 0
snowy-plugin-api/snowy-plugin-auth-api/pom.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>vip.xiaonuo</groupId>
+        <artifactId>snowy-plugin-api</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-plugin-auth-api</artifactId>
+    <packaging>jar</packaging>
+    <description>登录鉴权插件api接口</description>
+
+    <dependencies>
+        <!-- 每个插件接口都要引入common -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-common</artifactId>
+        </dependency>
+
+        <!-- sa-token-core -->
+        <dependency>
+            <groupId>cn.dev33</groupId>
+            <artifactId>sa-token-core</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 126 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.api;
+
+import cn.hutool.json.JSONObject;
+import vip.xiaonuo.auth.core.pojo.SaBaseClientLoginUser;
+import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
+
+import java.util.List;
+
+/**
+ * 登录用户API,由其他模块实现
+ *
+ * @author xuyuxiang
+ * @date 2021/12/23 21:48
+ */
+public interface SaBaseLoginUserApi {
+
+    /**
+     * 根据id获取B端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseLoginUser getUserById(String id);
+
+    /**
+     * 根据id获取C端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseClientLoginUser getClientUserById(String id);
+
+    /**
+     * 根据账号获取B端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseLoginUser getUserByAccount(String account);
+
+    /**
+     * 根据账号获取C端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseClientLoginUser getClientUserByAccount(String account);
+
+    /**
+     * 根据手机号获取B端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseLoginUser getUserByPhone(String phone);
+
+    /**
+     * 根据手机号获取C端用户信息,查不到则返回null
+     *
+     * @author xuyuxiang
+     * @date 2022/3/10 16:14
+     **/
+    SaBaseClientLoginUser getClientUserByPhone(String phone);
+
+    /**
+     * 根据用户id获取用户集合
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:53
+     */
+    List<JSONObject> listUserByUserIdList(List<String> userIdList);
+
+    /**
+     * 根据用户id获取角色集合
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:53
+     */
+    List<JSONObject> getRoleListByUserId(String userId);
+
+    /**
+     * 根据角色id和用户id集合获取按钮码集合
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:54
+     */
+    List<String> getButtonCodeListListByUserAndRoleIdList(List<String> userAndRoleIdList);
+
+    /**
+     * 根据角色id和用户id集合获取移动端按钮码集合
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:54
+     */
+    List<String> getMobileButtonCodeListListByUserIdAndRoleIdList(List<String> userAndRoleIdList);
+
+    /**
+     * 根据角色id和用户id集合获取权限集合
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:54
+     */
+    List<JSONObject> getPermissionListByUserIdAndRoleIdList(List<String> userAndRoleIdList, String orgId);
+
+    /**
+     * 更新用户的登录时间和登录ip等信息
+     *
+     * @author xuyuxiang
+     * @date 2022/4/27 22:57
+     */
+    void updateUserLoginInfo(String userId, String device);
+
+
+}

+ 35 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckLogin.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.annotation;
+
+import cn.dev33.satoken.annotation.SaCheckLogin;
+import vip.xiaonuo.auth.core.util.StpClientUtil;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 登录认证(前台User版):只有登录之后才能进入该方法
+ * 可标注在函数、类上(效果等同于标注在此类的所有方法上)
+ *
+ * @author xuyuxiang
+ * @date 2022/3/10 10:39
+ **/
+@SaCheckLogin(type = StpClientUtil.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE})
+public @interface SaClientCheckLogin {
+
+}

+ 51 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckPermission.java

@@ -0,0 +1,51 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.annotation;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaMode;
+import vip.xiaonuo.auth.core.util.StpClientUtil;
+import org.springframework.core.annotation.AliasFor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 权限认证(前台User版):必须具有指定权限才能进入该方法
+ * 可标注在函数、类上(效果等同于标注在此类的所有方法上)
+ *
+ * @author xuyuxiang
+ * @date 2022/3/10 10:40
+ **/
+@SaCheckPermission(type = StpClientUtil.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE})
+public @interface SaClientCheckPermission {
+
+    /**
+     * 需要校验的权限码
+     * @return 需要校验的权限码
+     */
+    @AliasFor(annotation = SaCheckPermission.class)
+    String [] value() default {};
+
+    /**
+     * 验证模式:AND | OR,默认AND
+     * @return 验证模式
+     */
+    @AliasFor(annotation = SaCheckPermission.class)
+    SaMode mode() default SaMode.AND;
+
+}

+ 52 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/annotation/SaClientCheckRole.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.annotation;
+
+
+import cn.dev33.satoken.annotation.SaCheckRole;
+import cn.dev33.satoken.annotation.SaMode;
+import vip.xiaonuo.auth.core.util.StpClientUtil;
+import org.springframework.core.annotation.AliasFor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 角色认证(前台User版):必须具有指定角色标识才能进入该方法
+ * 可标注在函数、类上(效果等同于标注在此类的所有方法上)
+ *
+ * @author xuyuxiang
+ * @date 2022/3/10 10:41
+ **/
+@SaCheckRole(type = StpClientUtil.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE})
+public @interface SaClientCheckRole {
+
+    /**
+     * 需要校验的角色标识
+     * @return 需要校验的角色标识
+     */
+    @AliasFor(annotation = SaCheckRole.class)
+    String [] value() default {};
+
+    /**
+     * 验证模式:AND | OR,默认AND
+     * @return 验证模式
+     */
+    @AliasFor(annotation = SaCheckRole.class)
+    SaMode mode() default SaMode.AND;
+
+}

+ 49 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/enums/SaClientTypeEnum.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.enums;
+
+import lombok.Getter;
+import vip.xiaonuo.common.exception.CommonException;
+
+/**
+ * 登录端类型枚举
+ *
+ * @author xuyuxiang
+ * @date 2021/10/11 14:02
+ **/
+@Getter
+public enum SaClientTypeEnum {
+
+    /**
+     * B端用户
+     */
+    B("B"),
+
+    /**
+     * C端用户
+     */
+    C("C");
+
+    private final String value;
+
+    SaClientTypeEnum(String value) {
+        this.value = value;
+    }
+
+    public static void validate(String value) {
+        boolean flag = B.getValue().equals(value) || C.getValue().equals(value);
+        if(!flag) {
+            throw new CommonException("不支持的登录端类型:{}", value);
+        }
+    }
+}

+ 233 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseClientLoginUser.java

@@ -0,0 +1,233 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.pojo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 基础的C端登录用户对象,可继承此类扩展更多属性
+ *
+ * @author xuyuxiang
+ * @date 2021/12/23 21:49
+ */
+@Getter
+@Setter
+public abstract class SaBaseClientLoginUser {
+
+    /** id */
+    @ApiModelProperty(value = "id", position = 1)
+    private String id;
+
+    /** 头像 */
+    @ApiModelProperty(value = "头像,图片base64", position = 2)
+    private String avatar;
+
+    /** 签名 */
+    @ApiModelProperty(value = "签名,图片base64", position = 3)
+    private String signature;
+
+    /** 账号 */
+    @ApiModelProperty(value = "账号", position = 4)
+    private String account;
+
+    /** 姓名 */
+    @ApiModelProperty(value = "姓名", position = 5)
+    private String name;
+
+    /** 昵称 */
+    @ApiModelProperty(value = "昵称", position = 6)
+    private String nickname;
+
+    /** 性别 */
+    @ApiModelProperty(value = "性别", position = 7)
+    private String gender;
+
+    /** 年龄 */
+    @ApiModelProperty(value = "年龄", position = 8)
+    private String age;
+
+    /** 出生日期 */
+    @ApiModelProperty(value = "出生日期", position = 9)
+    private String birthday;
+
+    /** 民族 */
+    @ApiModelProperty(value = "民族", position = 10)
+    private String nation;
+
+    /** 籍贯 */
+    @ApiModelProperty(value = "籍贯", position = 11)
+    private String nativePlace;
+
+    /** 家庭住址 */
+    @ApiModelProperty(value = "家庭住址", position = 12)
+    private String homeAddress;
+
+    /** 通信地址 */
+    @ApiModelProperty(value = "通信地址", position = 13)
+    private String mailingAddress;
+
+    /** 证件类型 */
+    @ApiModelProperty(value = "证件类型", position = 14)
+    private String idCardType;
+
+    /** 证件号码 */
+    @ApiModelProperty(value = "证件号码", position = 15)
+    private String idCardNumber;
+
+    /** 文化程度 */
+    @ApiModelProperty(value = "文化程度", position = 16)
+    private String cultureLevel;
+
+    /** 政治面貌 */
+    @ApiModelProperty(value = "政治面貌", position = 17)
+    private String politicalOutlook;
+
+    /** 毕业院校 */
+    @ApiModelProperty(value = "毕业院校", position = 18)
+    private String college;
+
+    /** 学历 */
+    @ApiModelProperty(value = "学历", position = 19)
+    private String education;
+
+    /** 学制 */
+    @ApiModelProperty(value = "学制", position = 20)
+    private String eduLength;
+
+    /** 学位 */
+    @ApiModelProperty(value = "学位", position = 21)
+    private String degree;
+
+    /** 手机 */
+    @ApiModelProperty(value = "手机", position = 22)
+    private String phone;
+
+    /** 邮箱 */
+    @ApiModelProperty(value = "邮箱", position = 23)
+    private String email;
+
+    /** 家庭电话 */
+    @ApiModelProperty(value = "家庭电话", position = 24)
+    private String homeTel;
+
+    /** 办公电话 */
+    @ApiModelProperty(value = "办公电话", position = 25)
+    private String officeTel;
+
+    /** 紧急联系人 */
+    @ApiModelProperty(value = "紧急联系人", position = 26)
+    private String emergencyContact;
+
+    /** 紧急联系人电话 */
+    @ApiModelProperty(value = "紧急联系人电话", position = 27)
+    private String emergencyPhone;
+
+    /** 紧急联系人地址 */
+    @ApiModelProperty(value = "紧急联系人地址", position = 28)
+    private String emergencyAddress;
+
+    /** 上次登录ip */
+    @ApiModelProperty(value = "上次登录ip", position = 29)
+    private String lastLoginIp;
+
+    /** 上次登录地点 */
+    @ApiModelProperty(value = "上次登录地点", position = 30)
+    private String lastLoginAddress;
+
+    /** 上次登录时间 */
+    @ApiModelProperty(value = "上次登录时间", position = 31)
+    private Date lastLoginTime;
+
+    /** 上次登录设备 */
+    @ApiModelProperty(value = "上次登录设备", position = 32)
+    private String lastLoginDevice;
+
+    /** 最新登录ip */
+    @ApiModelProperty(value = "最新登录ip", position = 33)
+    private String latestLoginIp;
+
+    /** 最新登录地点 */
+    @ApiModelProperty(value = "最新登录地点", position = 34)
+    private String latestLoginAddress;
+
+    /** 最新登录时间 */
+    @ApiModelProperty(value = "最新登录时间", position = 35)
+    private Date latestLoginTime;
+
+    /** 最新登录设备 */
+    @ApiModelProperty(value = "最新登录设备", position = 36)
+    private String latestLoginDevice;
+
+    /** 用户状态 */
+    @ApiModelProperty(value = "用户状态", position = 37)
+    private String userStatus;
+
+    /** 排序码 */
+    @ApiModelProperty(value = "排序码", position = 38)
+    private Integer sortCode;
+
+    /** 扩展信息 */
+    @ApiModelProperty(value = "扩展信息", position = 39)
+    private String extJson;
+
+    /** 按钮码集合 */
+    @ApiModelProperty(value = "按钮码集合", position = 40)
+    private List<String> buttonCodeList;
+
+    /** 移动端按钮码集合 */
+    @ApiModelProperty(value = "移动端按钮码集合", position = 41)
+    private List<String> mobileButtonCodeList;
+
+    /** 权限码集合 */
+    @ApiModelProperty(value = "权限码集合", position = 42, hidden = true)
+    private List<String> permissionCodeList;
+
+    /** 角色码集合 */
+    @ApiModelProperty(value = "角色码集合", position = 43, hidden = true)
+    private List<String> roleCodeList;
+
+    /** 数据范围集合 */
+    @ApiModelProperty(value = "数据范围集合", position = 44, hidden = true)
+    private List<DataScope> dataScopeList;
+
+    /** 用户密码hash值 */
+    @ApiModelProperty(value = "用户密码hash值", position = 45)
+    private String password;
+
+    /** 是否可登录,由继承类实现 */
+    public abstract Boolean getEnabled();
+
+    /**
+     * 数据范围类
+     *
+     * @author xuyuxiang
+     * @date 2022/8/15 13:57
+     **/
+    @Getter
+    @Setter
+    public static class DataScope {
+
+        /** API接口 */
+        @ApiModelProperty(value = "API接口", position = 1)
+        private String apiUrl;
+
+        /** 数据范围 */
+        @ApiModelProperty(value = "数据范围", position = 2)
+        private List<String> dataScope;
+    }
+}

+ 278 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/pojo/SaBaseLoginUser.java

@@ -0,0 +1,278 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.pojo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 基础的B端登录用户对象,可继承此类扩展更多属性
+ *
+ * @author xuyuxiang
+ * @date 2021/12/23 21:49
+ */
+@Getter
+@Setter
+public abstract class SaBaseLoginUser {
+
+    /** id */
+    @ApiModelProperty(value = "id", position = 1)
+    private String id;
+
+    /** 头像 */
+    @ApiModelProperty(value = "头像", position = 3)
+    private String avatar;
+
+    /** 签名 */
+    @ApiModelProperty(value = "签名", position = 4)
+    private String signature;
+
+    /** 账号 */
+    @ApiModelProperty(value = "账号", position = 5)
+    private String account;
+
+    /** 姓名 */
+    @ApiModelProperty(value = "姓名", position = 6)
+    private String name;
+
+    /** 昵称 */
+    @ApiModelProperty(value = "昵称", position = 7)
+    private String nickname;
+
+    /** 性别 */
+    @ApiModelProperty(value = "性别", position = 8)
+    private String gender;
+
+    /** 年龄 */
+    @ApiModelProperty(value = "年龄", position = 9)
+    private String age;
+
+    /** 出生日期 */
+    @ApiModelProperty(value = "出生日期", position = 10)
+    private String birthday;
+
+    /** 民族 */
+    @ApiModelProperty(value = "民族", position = 11)
+    private String nation;
+
+    /** 籍贯 */
+    @ApiModelProperty(value = "籍贯", position = 12)
+    private String nativePlace;
+
+    /** 家庭住址 */
+    @ApiModelProperty(value = "家庭住址", position = 13)
+    private String homeAddress;
+
+    /** 通信地址 */
+    @ApiModelProperty(value = "通信地址", position = 14)
+    private String mailingAddress;
+
+    /** 证件类型 */
+    @ApiModelProperty(value = "证件类型", position = 15)
+    private String idCardType;
+
+    /** 证件号码 */
+    @ApiModelProperty(value = "证件号码", position = 16)
+    private String idCardNumber;
+
+    /** 文化程度 */
+    @ApiModelProperty(value = "文化程度", position = 17)
+    private String cultureLevel;
+
+    /** 政治面貌 */
+    @ApiModelProperty(value = "政治面貌", position = 18)
+    private String politicalOutlook;
+
+    /** 毕业院校 */
+    @ApiModelProperty(value = "毕业院校", position = 19)
+    private String college;
+
+    /** 学历 */
+    @ApiModelProperty(value = "学历", position = 20)
+    private String education;
+
+    /** 学制 */
+    @ApiModelProperty(value = "学制", position = 21)
+    private String eduLength;
+
+    /** 学位 */
+    @ApiModelProperty(value = "学位", position = 22)
+    private String degree;
+
+    /** 手机 */
+    @ApiModelProperty(value = "手机", position = 23)
+    private String phone;
+
+    /** 邮箱 */
+    @ApiModelProperty(value = "邮箱", position = 24)
+    private String email;
+
+    /** 家庭电话 */
+    @ApiModelProperty(value = "家庭电话", position = 25)
+    private String homeTel;
+
+    /** 办公电话 */
+    @ApiModelProperty(value = "办公电话", position = 26)
+    private String officeTel;
+
+    /** 紧急联系人 */
+    @ApiModelProperty(value = "紧急联系人", position = 27)
+    private String emergencyContact;
+
+    /** 紧急联系人电话 */
+    @ApiModelProperty(value = "紧急联系人电话", position = 28)
+    private String emergencyPhone;
+
+    /** 紧急联系人地址 */
+    @ApiModelProperty(value = "紧急联系人地址", position = 29)
+    private String emergencyAddress;
+
+    /** 员工编号 */
+    @ApiModelProperty(value = "员工编号", position = 30)
+    private String empNo;
+
+    /** 入职日期 */
+    @ApiModelProperty(value = "入职日期", position = 31)
+    private String entryDate;
+
+    /** 组织id */
+    @ApiModelProperty(value = "组织id", position = 32)
+    private String orgId;
+    /** 组织id */
+    @ApiModelProperty(value = "组织id2", position = 32)
+    private List<String> orgIds;
+
+    /** 组织名称 */
+    @ApiModelProperty(value = "组织名称", position = 33)
+    private String orgName;
+    /** 组织名称 */
+    @ApiModelProperty(value = "组织名称2", position = 33)
+    private List<String> orgNames;
+
+    /** 职位id */
+    @ApiModelProperty(value = "职位id", position = 34)
+    private String positionId;
+
+    /** 职位名称 */
+    @ApiModelProperty(value = "职位名称", position = 35)
+    private String positionName;
+
+    /** 职级 */
+    @ApiModelProperty(value = "职级", position = 36)
+    private String positionLevel;
+
+    /** 主管id */
+    @ApiModelProperty(value = "主管id", position = 37)
+    private String directorId;
+
+    /** 兼任信息 */
+    @ApiModelProperty(value = "兼任信息", position = 38)
+    private String positionJson;
+
+    /** 上次登录ip */
+    @ApiModelProperty(value = "上次登录ip", position = 39)
+    private String lastLoginIp;
+
+    /** 上次登录地点 */
+    @ApiModelProperty(value = "上次登录地点", position = 40)
+    private String lastLoginAddress;
+
+    /** 上次登录时间 */
+    @ApiModelProperty(value = "上次登录时间", position = 41)
+    private Date lastLoginTime;
+
+    /** 上次登录设备 */
+    @ApiModelProperty(value = "上次登录设备", position = 42)
+    private String lastLoginDevice;
+
+    /** 最新登录ip */
+    @ApiModelProperty(value = "最新登录ip", position = 43)
+    private String latestLoginIp;
+
+    /** 最新登录地点 */
+    @ApiModelProperty(value = "最新登录地点", position = 44)
+    private String latestLoginAddress;
+
+    /** 最新登录时间 */
+    @ApiModelProperty(value = "最新登录时间", position = 45)
+    private Date latestLoginTime;
+
+    /** 最新登录设备 */
+    @ApiModelProperty(value = "最新登录设备", position = 46)
+    private String latestLoginDevice;
+
+    /** 用户状态 */
+    @ApiModelProperty(value = "用户状态", position = 47)
+    private String userStatus;
+
+    /** 排序码 */
+    @ApiModelProperty(value = "排序码", position = 48)
+    private Integer sortCode;
+
+    /** 扩展信息 */
+    @ApiModelProperty(value = "扩展信息", position = 49)
+    private String extJson;
+
+    /** 按钮码集合 */
+    @ApiModelProperty(value = "按钮码集合", position = 50)
+    private List<String> buttonCodeList;
+
+    /** 移动端按钮码集合 */
+    @ApiModelProperty(value = "移动端按钮码集合", position = 51)
+    private List<String> mobileButtonCodeList;
+
+    /** 权限码集合 */
+    @ApiModelProperty(value = "权限码集合", position = 52, hidden = true)
+    private List<String> permissionCodeList;
+
+    /** 角色码集合 */
+    @ApiModelProperty(value = "角色码集合", position = 53, hidden = true)
+    private List<String> roleCodeList;
+
+    /** 数据范围集合 */
+    @ApiModelProperty(value = "数据范围集合", position = 54, hidden = true)
+    private List<DataScope> dataScopeList;
+
+    /** 用户密码hash值 */
+    @ApiModelProperty(value = "用户密码hash值", position = 55)
+    private String password;
+
+    @ApiModelProperty(value = "账号类型", position = 51)
+    private Integer accountType;
+
+    /** 是否可登录,由继承类实现 */
+    public abstract Boolean getEnabled();
+
+    /**
+     * 数据范围类
+     *
+     * @author xuyuxiang
+     * @date 2022/8/15 13:57
+     **/
+    @Getter
+    @Setter
+    public static class DataScope {
+
+        /** API接口 */
+        @ApiModelProperty(value = "API接口", position = 1)
+        private String apiUrl;
+
+        /** 数据范围 */
+        @ApiModelProperty(value = "数据范围", position = 2)
+        private List<String> dataScope;
+    }
+}

+ 47 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpClientLoginUserUtil.java

@@ -0,0 +1,47 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.util;
+
+import cn.hutool.core.collection.CollectionUtil;
+import vip.xiaonuo.auth.core.pojo.SaBaseClientLoginUser;
+
+import java.util.List;
+
+/**
+ * C端登录用户工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/7/8 10:40
+ **/
+public class StpClientLoginUserUtil {
+
+    /**
+     * 获取当前C端登录用户
+     *
+     * @author xuyuxiang
+     * @date 2022/7/8 10:41
+     **/
+    public static SaBaseClientLoginUser getClientLoginUser() {
+        return (SaBaseClientLoginUser) StpClientUtil.getTokenSession().get("loginUser");
+    }
+
+    /**
+     * 获取当前C端登录用户的当前请求接口的数据范围(暂无数据范围)
+     *
+     * @author xuyuxiang
+     * @date 2022/7/8 10:41
+     **/
+    public static List<String> getLoginUserDataScope() {
+        return CollectionUtil.newArrayList();
+    }
+}

+ 936 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpClientUtil.java

@@ -0,0 +1,936 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.util;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.fun.SaFunction;
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.SaLoginModel;
+import cn.dev33.satoken.stp.SaTokenInfo;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.dev33.satoken.stp.StpUtil;
+
+import java.util.List;
+
+/**
+ * Sa-Token 权限认证工具类 (C端用户版)
+ *
+ * @author xuyuxiang
+ * @date 2022/3/10 10:43
+ **/
+public class StpClientUtil {
+
+    /**
+     * 账号类型标识
+     */
+    public static final String TYPE = "C";
+
+    /**
+     * 底层的 StpLogic 对象,使用匿名子类 重写`stpLogic对象`的一些方法
+     */
+    public static StpLogic stpLogic = new StpLogic(TYPE) {
+
+        /**
+         * 重写 StpLogic 类下的 `splicingKeyTokenName` 函数,返回一个与 `StpUtil` 相同的token名称
+         */
+        @Override
+        public String splicingKeyTokenName() {
+            return super.splicingKeyTokenName();
+        }
+
+        // 此处可以根据需求重写其他方法
+    };
+
+    /**
+     * 获取当前 StpLogic 的账号类型
+     * @return See Note
+     */
+    public static String getLoginType() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * 重置 StpLogic 对象
+     * @param stpLogic /
+     */
+    public static void setStpLogic(StpLogic stpLogic) {
+        StpUtil.stpLogic = stpLogic;
+        // 防止自定义 stpLogic 被覆盖
+        SaManager.putStpLogic(stpLogic);
+    }
+
+
+    // =================== 获取token 相关 ===================
+
+    /**
+     * 返回token名称
+     * @return 此StpLogic的token名称
+     */
+    public static String getTokenName() {
+        return stpLogic.getTokenName();
+    }
+
+    /**
+     * 在当前会话写入当前TokenValue
+     * @param tokenValue token值
+     */
+    public static void setTokenValue(String tokenValue) {
+        stpLogic.setTokenValue(tokenValue);
+    }
+
+    /**
+     * 在当前会话写入当前TokenValue
+     * @param tokenValue token值
+     * @param cookieTimeout Cookie存活时间(秒)
+     */
+    public static void setTokenValue(String tokenValue, int cookieTimeout) {
+        stpLogic.setTokenValue(tokenValue, cookieTimeout);
+    }
+
+    /**
+     * 获取当前TokenValue
+     * @return 当前tokenValue
+     */
+    public static String getTokenValue() {
+        return stpLogic.getTokenValue();
+    }
+
+    /**
+     * 获取当前TokenValue (不裁剪前缀)
+     * @return /
+     */
+    public static String getTokenValueNotCut() {
+        return stpLogic.getTokenValueNotCut();
+    }
+
+    /**
+     * 获取当前会话的Token信息
+     * @return token信息
+     */
+    public static SaTokenInfo getTokenInfo() {
+        return stpLogic.getTokenInfo();
+    }
+
+
+    // =================== 登录相关操作 ===================
+
+    // --- 登录
+
+    /**
+     * 会话登录
+     * @param id 账号id,建议的类型:(long | int | String)
+     */
+    public static void login(Object id) {
+        stpLogic.login(id);
+    }
+
+    /**
+     * 会话登录,并指定登录设备
+     * @param id 账号id,建议的类型:(long | int | String)
+     * @param device 设备标识
+     */
+    public static void login(Object id, String device) {
+        stpLogic.login(id, device);
+    }
+
+    /**
+     * 会话登录,并指定是否 [记住我]
+     * @param id 账号id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    public static void login(Object id, boolean isLastingCookie) {
+        stpLogic.login(id, isLastingCookie);
+    }
+
+    /**
+     * 会话登录,并指定所有登录参数Model
+     * @param id 登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    public static void login(Object id, SaLoginModel loginModel) {
+        stpLogic.login(id, loginModel);
+    }
+
+    /**
+     * 创建指定账号id的登录会话
+     * @param id 登录id,建议的类型:(long | int | String)
+     * @return 返回会话令牌
+     */
+    public static String createLoginSession(Object id) {
+        return stpLogic.createLoginSession(id);
+    }
+
+    /**
+     * 创建指定账号id的登录会话
+     * @param id 登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     * @return 返回会话令牌
+     */
+    public static String createLoginSession(Object id, SaLoginModel loginModel) {
+        return stpLogic.createLoginSession(id, loginModel);
+    }
+
+    // --- 注销
+
+    /**
+     * 会话注销
+     */
+    public static void logout() {
+        stpLogic.logout();
+    }
+
+    /**
+     * 会话注销,根据账号id
+     * @param loginId 账号id
+     */
+    public static void logout(Object loginId) {
+        stpLogic.logout(loginId);
+    }
+
+    /**
+     * 会话注销,根据账号id 和 设备标识
+     *
+     * @param loginId 账号id
+     * @param device 设备标识 (填null代表所有注销设备)
+     */
+    public static void logout(Object loginId, String device) {
+        stpLogic.logout(loginId, device);
+    }
+
+    /**
+     * 会话注销,根据指定 Token
+     *
+     * @param tokenValue 指定token
+     */
+    public static void logoutByTokenValue(String tokenValue) {
+        stpLogic.logoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 踢人下线,根据账号id
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     */
+    public static void kickout(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * 踢人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param loginId 账号id
+     * @param device 设备标识 (填null代表踢出所有设备)
+     */
+    public static void kickout(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+
+    /**
+     * 踢人下线,根据指定 Token
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
+     *
+     * @param tokenValue 指定token
+     */
+    public static void kickoutByTokenValue(String tokenValue) {
+        stpLogic.kickoutByTokenValue(tokenValue);
+    }
+
+    /**
+     * 顶人下线,根据账号id 和 设备标识
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
+     *
+     * @param loginId 账号id
+     * @param device 设备标识 (填null代表顶替所有设备)
+     */
+    public static void replaced(Object loginId, String device) {
+        stpLogic.replaced(loginId, device);
+    }
+
+
+    // 查询相关
+
+    /**
+     * 当前会话是否已经登录
+     * @return 是否已登录
+     */
+    public static boolean isLogin() {
+        return stpLogic.isLogin();
+    }
+
+    /**
+     * 检验当前会话是否已经登录,如未登录,则抛出异常
+     */
+    public static void checkLogin() {
+        stpLogic.checkLogin();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则抛出异常
+     * @return 账号id
+     */
+    public static Object getLoginId() {
+        return stpLogic.getLoginId();
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回默认值
+     * @param <T> 返回类型
+     * @param defaultValue 默认值
+     * @return 登录id
+     */
+    public static <T> T getLoginId(T defaultValue) {
+        return stpLogic.getLoginId(defaultValue);
+    }
+
+    /**
+     * 获取当前会话账号id, 如果未登录,则返回null
+     * @return 账号id
+     */
+    public static Object getLoginIdDefaultNull() {
+        return stpLogic.getLoginIdDefaultNull();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为String类型
+     * @return 账号id
+     */
+    public static String getLoginIdAsString() {
+        return stpLogic.getLoginIdAsString();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为int类型
+     * @return 账号id
+     */
+    public static int getLoginIdAsInt() {
+        return stpLogic.getLoginIdAsInt();
+    }
+
+    /**
+     * 获取当前会话账号id, 并转换为long类型
+     * @return 账号id
+     */
+    public static long getLoginIdAsLong() {
+        return stpLogic.getLoginIdAsLong();
+    }
+
+    /**
+     * 获取指定Token对应的账号id,如果未登录,则返回 null
+     * @param tokenValue token
+     * @return 账号id
+     */
+    public static Object getLoginIdByToken(String tokenValue) {
+        return stpLogic.getLoginIdByToken(tokenValue);
+    }
+
+    /**
+     * 获取Token扩展信息(只在jwt模式下有效)
+     * @param key 键值
+     * @return 对应的扩展数据
+     */
+    public static Object getExtra(String key) {
+        return stpLogic.getExtra(key);
+    }
+
+
+    // =================== User-Session 相关 ===================
+
+    /**
+     * 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     * @param loginId 账号id
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId, boolean isCreate) {
+        return stpLogic.getSessionByLoginId(loginId, isCreate);
+    }
+
+    /**
+     * 获取指定key的Session, 如果Session尚未创建,则返回null
+     * @param sessionId SessionId
+     * @return Session对象
+     */
+    public static SaSession getSessionBySessionId(String sessionId) {
+        return stpLogic.getSessionBySessionId(sessionId);
+    }
+
+    /**
+     * 获取指定账号id的Session,如果Session尚未创建,则新建并返回
+     * @param loginId 账号id
+     * @return Session对象
+     */
+    public static SaSession getSessionByLoginId(Object loginId) {
+        return stpLogic.getSessionByLoginId(loginId);
+    }
+
+    /**
+     * 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
+     * @param isCreate 是否新建
+     * @return Session对象
+     */
+    public static SaSession getSession(boolean isCreate) {
+        return stpLogic.getSession(isCreate);
+    }
+
+    /**
+     * 获取当前会话的Session,如果Session尚未创建,则新建并返回
+     * @return Session对象
+     */
+    public static SaSession getSession() {
+        return stpLogic.getSession();
+    }
+
+
+    // =================== Token-Session 相关 ===================
+
+    /**
+     * 获取指定Token-Session,如果Session尚未创建,则新建并返回
+     * @param tokenValue Token值
+     * @return Session对象
+     */
+    public static SaSession getTokenSessionByToken(String tokenValue) {
+        return stpLogic.getTokenSessionByToken(tokenValue);
+    }
+
+    /**
+     * 获取当前Token-Session,如果Session尚未创建,则新建并返回
+     * @return Session对象
+     */
+    public static SaSession getTokenSession() {
+        return stpLogic.getTokenSession();
+    }
+
+
+    // =================== [临时有效期] 验证相关 ===================
+
+    /**
+     * 检查当前token 是否已经[临时过期],如果已经过期则抛出异常
+     */
+    public static void checkActivityTimeout() {
+        stpLogic.checkActivityTimeout();
+    }
+
+    /**
+     * 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
+     * <h1>请注意: 即时token已经 [临时过期] 也可续签成功,
+     * 如果此场景下需要提示续签失败,可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
+     */
+    public static void updateLastActivityToNow() {
+        stpLogic.updateLastActivityToNow();
+    }
+
+
+    // =================== 过期时间相关 ===================
+
+    /**
+     * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
+     * @return token剩余有效时间
+     */
+    public static long getTokenTimeout() {
+        return stpLogic.getTokenTimeout();
+    }
+
+    /**
+     * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
+     * @return token剩余有效时间
+     */
+    public static long getSessionTimeout() {
+        return stpLogic.getSessionTimeout();
+    }
+
+    /**
+     * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
+     * @return token剩余有效时间
+     */
+    public static long getTokenSessionTimeout() {
+        return stpLogic.getTokenSessionTimeout();
+    }
+
+    /**
+     * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
+     * @return token [临时过期] 剩余有效时间
+     */
+    public static long getTokenActivityTimeout() {
+        return stpLogic.getTokenActivityTimeout();
+    }
+
+    /**
+     * 对当前 Token 的 timeout 值进行续期
+     * @param timeout 要修改成为的有效时间 (单位: 秒)
+     */
+    public static void renewTimeout(long timeout) {
+        stpLogic.renewTimeout(timeout);
+    }
+
+    /**
+     * 对指定 Token 的 timeout 值进行续期
+     * @param tokenValue 指定token
+     * @param timeout 要修改成为的有效时间 (单位: 秒)
+     */
+    public static void renewTimeout(String tokenValue, long timeout) {
+        stpLogic.renewTimeout(tokenValue, timeout);
+    }
+
+    // =================== 角色验证操作 ===================
+
+    /**
+     * 获取:当前账号的角色集合
+     * @return /
+     */
+    public static List<String> getRoleList() {
+        return stpLogic.getRoleList();
+    }
+
+    /**
+     * 获取:指定账号的角色集合
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getRoleList(Object loginId) {
+        return stpLogic.getRoleList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否拥有指定角色, 返回true或false
+     * @param role 角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(String role) {
+        return stpLogic.hasRole(role);
+    }
+
+    /**
+     * 判断:指定账号是否含有指定角色标识, 返回true或false
+     * @param loginId 账号id
+     * @param role 角色标识
+     * @return 是否含有指定角色标识
+     */
+    public static boolean hasRole(Object loginId, String role) {
+        return stpLogic.hasRole(loginId, role);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     * @param roleArray 角色标识数组
+     * @return true或false
+     */
+    public static boolean hasRoleAnd(String... roleArray) {
+        return stpLogic.hasRoleAnd(roleArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     * @param roleArray 角色标识数组
+     * @return true或false
+     */
+    public static boolean hasRoleOr(String... roleArray) {
+        return stpLogic.hasRoleOr(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
+     * @param role 角色标识
+     */
+    public static void checkRole(String role) {
+        stpLogic.checkRole(role);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleAnd(String... roleArray) {
+        stpLogic.checkRoleAnd(roleArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
+     * @param roleArray 角色标识数组
+     */
+    public static void checkRoleOr(String... roleArray) {
+        stpLogic.checkRoleOr(roleArray);
+    }
+
+
+    // =================== 权限验证操作 ===================
+
+    /**
+     * 获取:当前账号的权限码集合
+     * @return /
+     */
+    public static List<String> getPermissionList() {
+        return stpLogic.getPermissionList();
+    }
+
+    /**
+     * 获取:指定账号的权限码集合
+     * @param loginId 指定账号id
+     * @return /
+     */
+    public static List<String> getPermissionList(Object loginId) {
+        return stpLogic.getPermissionList(loginId);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, 返回true或false
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(String permission) {
+        return stpLogic.hasPermission(permission);
+    }
+
+    /**
+     * 判断:指定账号id是否含有指定权限, 返回true或false
+     * @param loginId 账号id
+     * @param permission 权限码
+     * @return 是否含有指定权限
+     */
+    public static boolean hasPermission(Object loginId, String permission) {
+        return stpLogic.hasPermission(loginId, permission);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限, [指定多个,必须全部具有]
+     * @param permissionArray 权限码数组
+     * @return true 或 false
+     */
+    public static boolean hasPermissionAnd(String... permissionArray) {
+        return stpLogic.hasPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 判断:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     * @param permissionArray 权限码数组
+     * @return true 或 false
+     */
+    public static boolean hasPermissionOr(String... permissionArray) {
+        return stpLogic.hasPermissionOr(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
+     * @param permission 权限码
+     */
+    public static void checkPermission(String permission) {
+        stpLogic.checkPermission(permission);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionAnd(String... permissionArray) {
+        stpLogic.checkPermissionAnd(permissionArray);
+    }
+
+    /**
+     * 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
+     * @param permissionArray 权限码数组
+     */
+    public static void checkPermissionOr(String... permissionArray) {
+        stpLogic.checkPermissionOr(permissionArray);
+    }
+
+
+    // =================== id 反查token 相关操作 ===================
+
+    /**
+     * 获取指定账号id的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     * @param loginId 账号id
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId) {
+        return stpLogic.getTokenValueByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue
+     * <p> 在配置为允许并发登录时,此方法只会返回队列的最后一个token,
+     * 如果你需要返回此账号id的所有token,请调用 getTokenValueListByLoginId
+     * @param loginId 账号id
+     * @param device 设备标识
+     * @return token值
+     */
+    public static String getTokenValueByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueByLoginId(loginId, device);
+    }
+
+    /**
+     * 获取指定账号id的tokenValue集合
+     * @param loginId 账号id
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId) {
+        return stpLogic.getTokenValueListByLoginId(loginId);
+    }
+
+    /**
+     * 获取指定账号id指定设备端的tokenValue 集合
+     * @param loginId 账号id
+     * @param device 设备标识
+     * @return 此loginId的所有相关token
+     */
+    public static List<String> getTokenValueListByLoginId(Object loginId, String device) {
+        return stpLogic.getTokenValueListByLoginId(loginId, device);
+    }
+
+    /**
+     * 返回当前会话的登录设备
+     * @return 当前令牌的登录设备
+     */
+    public static String getLoginDevice() {
+        return stpLogic.getLoginDevice();
+    }
+
+
+    // =================== 会话管理 ===================
+
+    /**
+     * 根据条件查询Token
+     * @param keyword 关键字
+     * @param start 开始处索引 (-1代表查询所有)
+     * @param size 获取数量
+     * @return token集合
+     */
+    public static List<String> searchTokenValue(String keyword, int start, int size, boolean sortType) {
+        return stpLogic.searchTokenValue(keyword, start, size, sortType);
+    }
+
+    /**
+     * 根据条件查询SessionId
+     * @param keyword 关键字
+     * @param start 开始处索引 (-1代表查询所有)
+     * @param size 获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchSessionId(String keyword, int start, int size, boolean sortType) {
+        return stpLogic.searchSessionId(keyword, start, size, sortType);
+    }
+
+    /**
+     * 根据条件查询Token专属Session的Id
+     * @param keyword 关键字
+     * @param start 开始处索引 (-1代表查询所有)
+     * @param size 获取数量
+     * @return sessionId集合
+     */
+    public static List<String> searchTokenSessionId(String keyword, int start, int size, boolean sortType) {
+        return stpLogic.searchTokenSessionId(keyword, start, size, sortType);
+    }
+
+
+    // ------------------- 账号封禁 -------------------
+
+    /**
+     * 封禁指定账号
+     * <p> 此方法不会直接将此账号id踢下线,而是在对方再次登录时抛出`DisableLoginException`异常
+     * @param loginId 指定账号id
+     * @param disableTime 封禁时间, 单位: 秒 (-1=永久封禁)
+     */
+    public static void disable(Object loginId, long disableTime) {
+        stpLogic.disable(loginId, disableTime);
+    }
+
+    /**
+     * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static boolean isDisable(Object loginId) {
+        return stpLogic.isDisable(loginId);
+    }
+
+    /**
+     * 获取指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
+     * @param loginId 账号id
+     * @return see note
+     */
+    public static long getDisableTime(Object loginId) {
+        return stpLogic.getDisableTime(loginId);
+    }
+
+    /**
+     * 解封指定账号
+     * @param loginId 账号id
+     */
+    public static void untieDisable(Object loginId) {
+        stpLogic.untieDisable(loginId);
+    }
+
+
+    // =================== 身份切换 ===================
+
+    /**
+     * 临时切换身份为指定账号id
+     * @param loginId 指定loginId
+     */
+    public static void switchTo(Object loginId) {
+        stpLogic.switchTo(loginId);
+    }
+
+    /**
+     * 结束临时切换身份
+     */
+    public static void endSwitch() {
+        stpLogic.endSwitch();
+    }
+
+    /**
+     * 当前是否正处于[身份临时切换]中
+     * @return 是否正处于[身份临时切换]中
+     */
+    public static boolean isSwitch() {
+        return stpLogic.isSwitch();
+    }
+
+    /**
+     * 在一个代码段里方法内,临时切换身份为指定账号id
+     * @param loginId 指定账号id
+     * @param function 要执行的方法
+     */
+    public static void switchTo(Object loginId, SaFunction function) {
+        stpLogic.switchTo(loginId, function);
+    }
+
+
+    // ------------------- 二级认证 -------------------
+
+    /**
+     * 在当前会话 开启二级认证
+     * @param safeTime 维持时间 (单位: 秒)
+     */
+    public static void openSafe(long safeTime) {
+        stpLogic.openSafe(safeTime);
+    }
+
+    /**
+     * 当前会话 是否处于二级认证时间内
+     * @return true=二级认证已通过, false=尚未进行二级认证或认证已超时
+     */
+    public static boolean isSafe() {
+        return stpLogic.isSafe();
+    }
+
+    /**
+     * 检查当前会话是否已通过二级认证,如未通过则抛出异常
+     */
+    public static void checkSafe() {
+        stpLogic.checkSafe();
+    }
+
+    /**
+     * 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
+     * @return 剩余有效时间
+     */
+    public static long getSafeTime() {
+        return stpLogic.getSafeTime();
+    }
+
+    /**
+     * 在当前会话 结束二级认证
+     */
+    public static void closeSafe() {
+        stpLogic.closeSafe();
+    }
+
+
+    // =================== 历史API,兼容旧版本 ===================
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.getLoginType() ,使用方式保持不变 </h1>
+     *
+     * 获取当前StpLogin的loginKey
+     * @return 当前StpLogin的loginKey
+     */
+    @Deprecated
+    public static String getLoginKey() {
+        return stpLogic.getLoginType();
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     *
+     * 在当前会话上登录id
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId) {
+        stpLogic.login(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     *
+     * 在当前会话上登录id, 并指定登录设备
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     * @param device 设备标识
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, String device) {
+        stpLogic.login(loginId, device);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     *
+     * 在当前会话上登录id, 并指定登录设备
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     * @param isLastingCookie 是否为持久Cookie
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, boolean isLastingCookie) {
+        stpLogic.login(loginId, isLastingCookie);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.login() ,使用方式保持不变 </h1>
+     *
+     * 在当前会话上登录id, 并指定所有登录参数Model
+     * @param loginId 登录id,建议的类型:(long | int | String)
+     * @param loginModel 此次登录的参数Model
+     */
+    @Deprecated
+    public static void setLoginId(Object loginId, SaLoginModel loginModel) {
+        stpLogic.login(loginId, loginModel);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     *
+     * 会话注销,根据账号id (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2
+     * @param loginId 账号id
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId) {
+        stpLogic.kickout(loginId);
+    }
+
+    /**
+     * <h1> 本函数设计已过时,未来版本可能移除此函数,请及时更换为 StpUtil.kickout() ,使用方式保持不变 </h1>
+     *
+     * 会话注销,根据账号id and 设备标识 (踢人下线)
+     * <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-2 </p>
+     * @param loginId 账号id
+     * @param device 设备标识 (填null代表所有注销设备)
+     */
+    @Deprecated
+    public static void logoutByLoginId(Object loginId, String device) {
+        stpLogic.kickout(loginId, device);
+    }
+}

+ 55 - 0
snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/core/util/StpLoginUserUtil.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.auth.core.util;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
+import vip.xiaonuo.common.util.CommonServletUtil;
+
+import java.util.List;
+
+/**
+ * B端登录用户工具类
+ *
+ * @author xuyuxiang
+ * @date 2022/7/8 10:40
+ **/
+public class StpLoginUserUtil {
+
+    /**
+     * 获取当前B端登录用户
+     *
+     * @author xuyuxiang
+     * @date 2022/7/8 10:41
+     **/
+    public static SaBaseLoginUser getLoginUser() {
+        return (SaBaseLoginUser) StpUtil.getTokenSession().get("loginUser");
+    }
+
+    /**
+     * 获取当前B端登录用户的当前请求接口的数据范围
+     *
+     * @author xuyuxiang
+     * @date 2022/7/8 10:41
+     **/
+    public static List<String> getLoginUserDataScope() {
+        List<String> resultList = CollectionUtil.newArrayList();
+        getLoginUser().getDataScopeList().forEach(dataScope -> {
+            if(dataScope.getApiUrl().equals(CommonServletUtil.getRequest().getServletPath())) {
+                resultList.addAll(dataScope.getDataScope());
+            }
+        });
+        return resultList;
+    }
+}

+ 1 - 0
snowy-plugin-api/snowy-plugin-biz-api/README.md

@@ -0,0 +1 @@
+# 业务功能插件api接口

+ 24 - 0
snowy-plugin-api/snowy-plugin-biz-api/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>vip.xiaonuo</groupId>
+        <artifactId>snowy-plugin-api</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-plugin-biz-api</artifactId>
+    <packaging>jar</packaging>
+    <description>业务功能插件api接口</description>
+
+    <dependencies>
+        <!-- 每个插件接口都要引入common -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-common</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 13 - 0
snowy-plugin-api/snowy-plugin-biz-api/src/main/java/vip/xiaonuo/biz/package-info.java

@@ -0,0 +1,13 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.biz;

+ 1 - 0
snowy-plugin-api/snowy-plugin-client-api/README.md

@@ -0,0 +1 @@
+# C端功能插件api接口

+ 24 - 0
snowy-plugin-api/snowy-plugin-client-api/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>vip.xiaonuo</groupId>
+        <artifactId>snowy-plugin-api</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-plugin-client-api</artifactId>
+    <packaging>jar</packaging>
+    <description>C端功能插件api接口</description>
+
+    <dependencies>
+        <!-- 每个插件接口都要引入common -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-common</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 13 - 0
snowy-plugin-api/snowy-plugin-client-api/src/main/java/vip/xiaonuo/client/package-info.java

@@ -0,0 +1,13 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.client;

+ 1 - 0
snowy-plugin-api/snowy-plugin-dev-api/README.md

@@ -0,0 +1 @@
+# 开发工具插件api接口

+ 108 - 0
snowy-plugin-api/snowy-plugin-dev-api/pom.xml

@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>vip.xiaonuo</groupId>
+        <artifactId>snowy-plugin-api</artifactId>
+        <version>2.0.0</version>
+    </parent>
+
+    <artifactId>snowy-plugin-dev-api</artifactId>
+    <packaging>jar</packaging>
+    <description>开发工具插件api接口</description>
+
+    <dependencies>
+        <!-- 每个插件接口都要引入common -->
+        <dependency>
+            <groupId>vip.xiaonuo</groupId>
+            <artifactId>snowy-common</artifactId>
+        </dependency>
+
+        <!--腾讯云上传文件客户端-->
+        <dependency>
+            <groupId>com.qcloud</groupId>
+            <artifactId>cos_api</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>tencentcloud-sdk-java-common</artifactId>
+                    <groupId>com.tencentcloudapi</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--阿里云上传文件客户端-->
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.codehaus.jettison</groupId>
+                    <artifactId>jettison</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- jettison -->
+        <dependency>
+            <groupId>org.codehaus.jettison</groupId>
+            <artifactId>jettison</artifactId>
+        </dependency>
+
+        <!--minio上传文件客户端-->
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+        </dependency>
+
+        <!--java邮件发送-->
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+        </dependency>
+
+        <!--阿里云邮件发送-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-dm</artifactId>
+        </dependency>
+
+        <!-- 腾讯云邮件发送 -->
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-ses</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>okio-jvm</artifactId>
+                    <groupId>com.squareup.okio</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--阿里云短信发送-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>org.jacoco.agent</artifactId>
+                    <groupId>org.jacoco</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!--腾讯云短信发送-->
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java-sms</artifactId>
+        </dependency>
+
+        <!--系统硬件信息-->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 30 - 0
snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevConfigApi.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.dev.api;
+
+/**
+ * 配置APi接口
+ *
+ * @author xuyuxiang
+ * @date 2022/6/17 10:37
+ **/
+public interface DevConfigApi {
+
+    /**
+     * 根据键获取值
+     *
+     * @author xuyuxiang
+     * @date 2022/6/17 11:11
+     **/
+    String getValueByKey(String key);
+}

+ 22 - 0
snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevDictApi.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright [2022] [https://www.baiduc.com]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.baiduc.com
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.baiduc.com
+ */
+package vip.xiaonuo.dev.api;
+
+/**
+ * 字典API
+ *
+ * @author xuyuxiang
+ * @date 2022/9/2 15:58
+ */
+public interface DevDictApi {
+}

+ 0 - 0
snowy-plugin-api/snowy-plugin-dev-api/src/main/java/vip/xiaonuo/dev/api/DevEmailApi.java


Деякі файли не було показано, через те що забагато файлів було змінено