FengChaoYu преди 3 месеца
ревизия
8a94c40244
променени са 100 файла, в които са добавени 8925 реда и са изтрити 0 реда
  1. 38 0
      .gitignore
  2. 252 0
      pom.xml
  3. 30 0
      src/main/java/com/gree/mall/contest/ManagerApplication.java
  4. 11 0
      src/main/java/com/gree/mall/contest/annotation/ApiNotAuth.java
  5. 12 0
      src/main/java/com/gree/mall/contest/annotation/DataPermission.java
  6. 26 0
      src/main/java/com/gree/mall/contest/annotation/ZfireField.java
  7. 10 0
      src/main/java/com/gree/mall/contest/annotation/ZfireList.java
  8. 28 0
      src/main/java/com/gree/mall/contest/bean/ExcelData.java
  9. 30 0
      src/main/java/com/gree/mall/contest/bean/ExcelExamData.java
  10. 14 0
      src/main/java/com/gree/mall/contest/bean/SVerification.java
  11. 60 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminFastEntryVO.java
  12. 18 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminModuleBean.java
  13. 20 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminModuleTree.java
  14. 70 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminModuleVO.java
  15. 18 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminRoleBean.java
  16. 26 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminUserBean.java
  17. 28 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminUserCom.java
  18. 19 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminWebsitGrantBean.java
  19. 15 0
      src/main/java/com/gree/mall/contest/bean/admin/AdminWebsitTree.java
  20. 11 0
      src/main/java/com/gree/mall/contest/bean/admin/reqDto/AdminUserAddReqBean.java
  21. 43 0
      src/main/java/com/gree/mall/contest/bean/common/AmityUrlVO.java
  22. 52 0
      src/main/java/com/gree/mall/contest/bean/common/CarouselMapVO.java
  23. 41 0
      src/main/java/com/gree/mall/contest/bean/common/SysDictVO.java
  24. 20 0
      src/main/java/com/gree/mall/contest/bean/common/WechatOpenBean.java
  25. 19 0
      src/main/java/com/gree/mall/contest/bean/common/WxJsApiSignBean.java
  26. 73 0
      src/main/java/com/gree/mall/contest/bean/manage/NotifyRecordVO.java
  27. 46 0
      src/main/java/com/gree/mall/contest/bean/manage/NotifyVO.java
  28. 82 0
      src/main/java/com/gree/mall/contest/bean/manage/UserTopPopVO.java
  29. 20 0
      src/main/java/com/gree/mall/contest/bean/zfire/QueryParamBean.java
  30. 25 0
      src/main/java/com/gree/mall/contest/bean/zfire/ZfireCustomParam.java
  31. 99 0
      src/main/java/com/gree/mall/contest/bean/zfire/ZfireParamBean.java
  32. 27 0
      src/main/java/com/gree/mall/contest/commonmapper/AdminMapper.java
  33. 62 0
      src/main/java/com/gree/mall/contest/commonmapper/CommonMapper.java
  34. 42 0
      src/main/java/com/gree/mall/contest/config/BaseEnumConverterFactory.java
  35. 64 0
      src/main/java/com/gree/mall/contest/config/KaptchaConfig.java
  36. 20 0
      src/main/java/com/gree/mall/contest/config/MybatisPlusConfig.java
  37. 111 0
      src/main/java/com/gree/mall/contest/config/RedisConfig.java
  38. 17 0
      src/main/java/com/gree/mall/contest/config/RedisLockConfiguration.java
  39. 96 0
      src/main/java/com/gree/mall/contest/config/SwaggerUIConfig.java
  40. 37 0
      src/main/java/com/gree/mall/contest/config/aop/AopConfig.java
  41. 105 0
      src/main/java/com/gree/mall/contest/config/aop/ApiAspect.java
  42. 119 0
      src/main/java/com/gree/mall/contest/config/aop/OperationLogAspect.java
  43. 366 0
      src/main/java/com/gree/mall/contest/config/aop/ZfireFiledAop.java
  44. 119 0
      src/main/java/com/gree/mall/contest/config/wx/WxConfiguration.java
  45. 63 0
      src/main/java/com/gree/mall/contest/config/wx/WxMaLettuceRedisConfig.java
  46. 67 0
      src/main/java/com/gree/mall/contest/config/wx/WxMpLettuceRedisConfig.java
  47. 63 0
      src/main/java/com/gree/mall/contest/constant/Constant.java
  48. 16 0
      src/main/java/com/gree/mall/contest/constant/SysDictConstant.java
  49. 60 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/AdminFastEntryController.java
  50. 54 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/AdminModuleController.java
  51. 75 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/AdminRoleController.java
  52. 233 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/AdminUserController.java
  53. 107 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/AdminWebsitController.java
  54. 61 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/OperationLogController.java
  55. 47 0
      src/main/java/com/gree/mall/contest/controller/pc/admin/SysConfigController.java
  56. 166 0
      src/main/java/com/gree/mall/contest/controller/pc/common/CommonController.java
  57. 115 0
      src/main/java/com/gree/mall/contest/controller/pc/zfire/ZfireController.java
  58. 46 0
      src/main/java/com/gree/mall/contest/convert/BaseEnumDeserializer.java
  59. 28 0
      src/main/java/com/gree/mall/contest/enums/IsEnum.java
  60. 22 0
      src/main/java/com/gree/mall/contest/enums/IsYesNoEnum.java
  61. 22 0
      src/main/java/com/gree/mall/contest/enums/MiniPortEnum.java
  62. 28 0
      src/main/java/com/gree/mall/contest/enums/StateEnum.java
  63. 28 0
      src/main/java/com/gree/mall/contest/enums/StatusEnum.java
  64. 24 0
      src/main/java/com/gree/mall/contest/enums/admin/AdminWebsitTypeEnum.java
  65. 32 0
      src/main/java/com/gree/mall/contest/enums/admin/BalanceOrderTypeEnum.java
  66. 71 0
      src/main/java/com/gree/mall/contest/enums/admin/RoleTypeEnum.java
  67. 30 0
      src/main/java/com/gree/mall/contest/enums/admin/UpdateLogRemarkEnum.java
  68. 222 0
      src/main/java/com/gree/mall/contest/enums/base/BaseEnum.java
  69. 45 0
      src/main/java/com/gree/mall/contest/enums/base/BaseEnumDeserializer.java
  70. 38 0
      src/main/java/com/gree/mall/contest/enums/manage/NoticeTypeEnum.java
  71. 21 0
      src/main/java/com/gree/mall/contest/enums/manage/NotifyTypeEnum.java
  72. 23 0
      src/main/java/com/gree/mall/contest/enums/manage/PopJumpTypeEnum.java
  73. 19 0
      src/main/java/com/gree/mall/contest/enums/manage/PopStatusEnum.java
  74. 25 0
      src/main/java/com/gree/mall/contest/enums/manage/SendObjEnum.java
  75. 38 0
      src/main/java/com/gree/mall/contest/exception/RemoteServiceException.java
  76. 97 0
      src/main/java/com/gree/mall/contest/helper/ResponseHelper.java
  77. 212 0
      src/main/java/com/gree/mall/contest/logic/SMSLogic.java
  78. 78 0
      src/main/java/com/gree/mall/contest/logic/admin/AdminFastEntryLogic.java
  79. 100 0
      src/main/java/com/gree/mall/contest/logic/admin/AdminModuleLogic.java
  80. 119 0
      src/main/java/com/gree/mall/contest/logic/admin/AdminRoleLogic.java
  81. 600 0
      src/main/java/com/gree/mall/contest/logic/admin/AdminUserLogic.java
  82. 302 0
      src/main/java/com/gree/mall/contest/logic/admin/AdminWebsitLogic.java
  83. 75 0
      src/main/java/com/gree/mall/contest/logic/admin/OperationLogLogic.java
  84. 39 0
      src/main/java/com/gree/mall/contest/logic/admin/SysConfigLogic.java
  85. 316 0
      src/main/java/com/gree/mall/contest/logic/common/CommonLogic.java
  86. 243 0
      src/main/java/com/gree/mall/contest/logic/common/WechatLogic.java
  87. 31 0
      src/main/java/com/gree/mall/contest/logic/wxmp/WxMpMessageLogic.java
  88. 51 0
      src/main/java/com/gree/mall/contest/utils/ApplicationContextUtils.java
  89. 317 0
      src/main/java/com/gree/mall/contest/utils/CommonUtils.java
  90. 46 0
      src/main/java/com/gree/mall/contest/utils/IpUtil.java
  91. 193 0
      src/main/java/com/gree/mall/contest/utils/JwtUtils.java
  92. 608 0
      src/main/java/com/gree/mall/contest/utils/RedisUtil.java
  93. 244 0
      src/main/java/com/gree/mall/contest/utils/VerifiUtils.java
  94. 190 0
      src/main/java/com/gree/mall/contest/utils/oss/OSSUtil.java
  95. 466 0
      src/main/java/com/gree/mall/contest/utils/zfire/FieldUtils.java
  96. 78 0
      src/main/resources/application-dev.properties
  97. 73 0
      src/main/resources/application-prd.properties
  98. 66 0
      src/main/resources/application-test.properties
  99. 47 0
      src/main/resources/application.properties
  100. 4 0
      src/main/resources/generator.properties

+ 38 - 0
.gitignore

@@ -0,0 +1,38 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+*.log
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+/src/main/java/com/gree/mall/manager/plus/
+/src/test/
+.DS_Store
+logs/

+ 252 - 0
pom.xml

@@ -0,0 +1,252 @@
+<?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>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.18</version>
+    </parent>
+
+    <groupId>com.zfire.mall</groupId>
+    <artifactId>mall-contest</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+        <spring-boot.version>2.7.18</spring-boot.version>
+        <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
+        <swagger-v3.version>2.2.25</swagger-v3.version>
+        <springdoc.version>1.8.0</springdoc.version>
+        <hutool.version>5.8.21</hutool.version>
+        <druid.version>1.2.16</druid.version>
+        <jwt.version>0.12.6</jwt.version>
+        <google-z.version>3.3.2</google-z.version>
+        <knife4j.version>4.3.0</knife4j.version>
+        <mapstruct.version>1.5.5.Final</mapstruct.version>
+        <weixin.version>4.5.0</weixin.version>
+        <kaptcha.version>2.3.2</kaptcha.version>
+        <aliyun.sdk.version>4.0.3</aliyun.sdk.version>
+        <aliyun.oss.version>3.8.1</aliyun.oss.version>
+        <ali.easyexcel.version>3.3.2</ali.easyexcel.version>
+        <poi.version>3.17</poi.version>
+    </properties>
+
+    <dependencies>
+        <!-- spring boot -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-json</artifactId>
+            <version>${spring-boot.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>${spring-boot.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <version>${spring-boot.version}</version>
+        </dependency>
+        <!-- Lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>${lombok.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <!-- Hutool -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+        <!-- Hibernate Validator -->
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>${hibernate-validator.version}</version>
+        </dependency>
+        <!-- MyBatis Plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+        <!-- mysql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.version}</version>
+        </dependency>
+        <!-- swagger -->
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>${swagger-v3.version}</version>
+        </dependency>
+        <!-- 接口文档:使用最新版本的 Swagger 模型 -->
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>${swagger-v3.version}</version>
+        </dependency>
+        <!-- 接口文档 UI:knife4j -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+            <version>${knife4j.version}</version>
+        </dependency>
+        <!-- Druid -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>${druid.version}</version>
+        </dependency>
+        <!-- jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <!-- mapstruct -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>${mapstruct.version}</version>
+        </dependency>
+        <!-- 图片验证码 -->
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+            <version>${kaptcha.version}</version>
+        </dependency>
+        <!-- 微信支付 -->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-pay</artifactId>
+            <version>${weixin.version}</version>
+        </dependency>
+        <!-- 微信小程序 -->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-miniapp</artifactId>
+            <version>${weixin.version}</version>
+        </dependency>
+        <!--公众号-->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-mp</artifactId>
+            <version>${weixin.version}</version>
+        </dependency>
+        <!-- 阿里 -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>${aliyun.sdk.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>${ali.easyexcel.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>${aliyun.oss.version}</version>
+        </dependency>
+        <!-- JWT -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>${jwt.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>${jwt.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+        <!-- mapstruct -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>${mapstruct.version}</version>
+        </dependency>
+        <!-- poi -->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi</artifactId>-->
+<!--            <version>${poi.version}</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml-schemas</artifactId>-->
+<!--            <version>${poi.version}</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>${poi.version}</version>-->
+<!--        </dependency>-->
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.plus.plugin</groupId>
+                <artifactId>plus-maven-plugin</artifactId>
+                <version>1.0</version>
+                <configuration>
+                    <configPath>src/main/resources/generator.properties</configPath>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 30 - 0
src/main/java/com/gree/mall/contest/ManagerApplication.java

@@ -0,0 +1,30 @@
+package com.gree.mall.contest;
+
+import com.gree.mall.contest.utils.ApplicationContextUtils;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@ServletComponentScan(basePackages = {"com.gree.mall.contest"})
+@EnableAsync
+@EnableScheduling
+@EnableCaching
+@MapperScan(basePackages = {
+        "com.gree.mall.contest.plus.mapper",
+        "com.gree.mall.contest.commonmapper"
+})
+public class ManagerApplication {
+
+    public static void main(String[] args) {
+        ApplicationContext context = SpringApplication.run(ManagerApplication.class, args);
+        ApplicationContextUtils.setApplicationContext(context);
+    }
+
+
+}

+ 11 - 0
src/main/java/com/gree/mall/contest/annotation/ApiNotAuth.java

@@ -0,0 +1,11 @@
+package com.gree.mall.contest.annotation;
+
+import java.lang.annotation.*;
+
+@Target(value = ElementType.METHOD)
+@Retention(value = RetentionPolicy.RUNTIME)
+@Documented
+public @interface ApiNotAuth {
+
+
+}

+ 12 - 0
src/main/java/com/gree/mall/contest/annotation/DataPermission.java

@@ -0,0 +1,12 @@
+package com.gree.mall.contest.annotation;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataPermission {
+
+    String field() default "company_wechat_id";
+}

+ 26 - 0
src/main/java/com/gree/mall/contest/annotation/ZfireField.java

@@ -0,0 +1,26 @@
+package com.gree.mall.contest.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.FIELD,ElementType.TYPE})
+@Retention(value = RetentionPolicy.RUNTIME)
+@Documented
+public @interface ZfireField {
+
+    String tbName() default "";//表简称或全称
+    String colName() default "";//表字段
+    String type() default "input";//input=输入框 select=下拉框
+    String frontCode() default "";//前端特定用
+    String fixed() default ""; //left固定左侧,right固定右侧,默认不固定
+    boolean pk() default false; //是否为列表主键id
+    boolean isShow() default true;
+    boolean isTotal() default false;//汇总
+    boolean hide() default false;
+    boolean isQuery() default true;//是否支持查询
+    boolean multiple() default false;//是否支持多选
+    boolean isCutting() default true;//是否支持切割
+    int sortNum() default 999;
+
+    boolean ignoreSelect() default false;//是否忽略查询该字段
+
+}

+ 10 - 0
src/main/java/com/gree/mall/contest/annotation/ZfireList.java

@@ -0,0 +1,10 @@
+package com.gree.mall.contest.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.METHOD})
+@Retention(value = RetentionPolicy.RUNTIME)
+@Documented
+public @interface ZfireList {
+
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/bean/ExcelData.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class ExcelData implements Serializable {
+    /**
+     * 表头
+     */
+    @Schema(description = "表头")
+    private List<String> titles;
+
+    /**
+     * 数据
+     */
+    @Schema(description = "数据")
+    private List<List<Object>> rows;
+
+    /**
+     * 页签名称
+     */
+    @Schema(description = "页签名称")
+    private String name;
+}

+ 30 - 0
src/main/java/com/gree/mall/contest/bean/ExcelExamData.java

@@ -0,0 +1,30 @@
+package com.gree.mall.contest.bean;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ExcelExamData{
+    /**
+     * 表头
+     */
+    private List<ExcelTitle> excelTitleBean;
+    /**
+     * 数据
+     */
+    private List<List<Object>> rows;
+    /**
+     * 页签名称
+     */
+    private String name;
+
+
+    @Data
+    public static class ExcelTitle{
+        private String title;
+        private List<String> childTitle;
+        private Short colorIndex;
+    }
+
+}

+ 14 - 0
src/main/java/com/gree/mall/contest/bean/SVerification.java

@@ -0,0 +1,14 @@
+package com.gree.mall.contest.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class SVerification implements Serializable {
+    @Schema(name = "code", description = "code")
+    private String code;
+    @Schema(name = "pic", description = "64位编码")
+    private String pic;//64位编码
+}

+ 60 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminFastEntryVO.java

@@ -0,0 +1,60 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.StatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@ZfireField(tbName = "a")
+@Data
+public class AdminFastEntryVO {
+
+    @ZfireField(hide = true)
+    private String id;
+
+    @Schema(description = "系统名称")
+    private String sysName;
+
+    @Schema(description  = "展示名称")
+    private String name;
+
+    @Schema(description  = "链接地址")
+    private String url;
+
+    @Schema(description  = "图标")
+    private String icon;
+
+    @Schema(description  = "是否展示")
+    private StatusEnum status;
+
+    @Schema(description  = "可否点击")
+    private StatusEnum status2;
+
+    @Schema(description  = "类型")
+    private String type;
+
+    @Schema(description  = "code")
+    private String code;
+
+    @Schema(description  = "排序")
+    private Integer sort;
+
+    @Schema(description  = "全路径")
+    private String fullUrl;
+
+
+    @Schema(description  = "创建人")
+    private String createBy;
+
+    @Schema(description  = "创建时间")
+    private Date createTime;
+
+    @Schema(description  = "修改人")
+    private String updateBy;
+
+    @Schema(description  = "修改时间")
+    private Date updateTime;
+
+}

+ 18 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminModuleBean.java

@@ -0,0 +1,18 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminModule;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AdminModuleBean extends AdminModule {
+
+    @Schema(description = "按钮子集")
+    private List<AdminModule> childList;
+
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminModuleTree.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminModule;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AdminModuleTree extends AdminModule {
+
+    @Schema(description = "子级")
+    private List<AdminModuleTree> children = new ArrayList<>();
+    @Schema(description = "是否显示")
+    private Boolean show = false;
+
+}

+ 70 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminModuleVO.java

@@ -0,0 +1,70 @@
+package com.gree.mall.contest.bean.admin;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author qinrongjun
+ * @description
+ * @date 2023/8/12 14:55 星期六
+ */
+@Data
+public class AdminModuleVO {
+
+    @Schema(description = "id")
+    private String moduleId;
+
+    @Schema(description = "名称")
+    private String moduleName;
+
+    @Schema(description = "模块路径")
+    private String modulePath;
+
+    @Schema(description = "true=正常 false=作废")
+    private Boolean status;
+
+    @Schema(description = "1=普通菜单 2=功能菜单 3=功能点  4=外部菜单")
+    private Integer type;
+
+    @Schema(description = "编号")
+    private String code;
+
+    @Schema(description = "层级")
+    private Integer level;
+
+    @Schema(description = "url")
+    private String url;
+
+    @Schema(description = "全url")
+    private String fullUrl;
+
+    @Schema(description = "当前url")
+    private String curUrl;
+
+    @Schema(description = "父id")
+    private String parentId;
+
+    @Schema(description = "父级名称")
+    private String parentModule;
+
+    @Schema(description = "排序号")
+    private Integer sortNum;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "icon")
+    private String icon;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+
+    @Schema(description = "type=2的功能点")
+    List<AdminModuleVO> childList;
+
+    @Schema(description = "是否有缓存")
+    private Boolean isCache;
+}

+ 18 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminRoleBean.java

@@ -0,0 +1,18 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminRole;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author :lijh
+ * @description:TODO
+ * @date :2024/3/11 18:52
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AdminRoleBean extends AdminRole {
+    @Schema(description = "账号数量")
+    private Long accountNum;
+}

+ 26 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminUserBean.java

@@ -0,0 +1,26 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminUser;
+import com.gree.mall.contest.plus.entity.Merchant;
+import com.gree.mall.contest.plus.entity.ServiceProvider;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AdminUserBean extends AdminUser {
+
+    private String token;
+
+    @Schema(description = "token到期时间")
+    private Date expireTimeToken;
+
+    @Schema(description = "服务商")
+    private ServiceProvider serviceProvider;
+
+    @Schema(description = "合作商户")
+    private Merchant merchant;
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminUserCom.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminCompanyWechat;
+import com.gree.mall.contest.plus.entity.AdminUser;
+import com.gree.mall.contest.plus.entity.AdminWebsit;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class AdminUserCom extends AdminUser {
+
+    @Schema(description = "部门")
+    private AdminWebsit adminWebsit;
+    @Schema(description = "部门id")
+    private List<String> adminWebsitIds;
+    @Schema(description = "只读帐号")
+    private Boolean onlyRead = false;
+    @Schema(description = "商户ids")
+    private List<String> companyWechatIds;
+    @Schema(description = "当前登录人所使用的商户信息")
+    private AdminCompanyWechat adminCompanyWechat;
+
+    @Schema(description = "所有区id(平台业务员用)")
+    private List<String> areaIds;
+
+}

+ 19 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminWebsitGrantBean.java

@@ -0,0 +1,19 @@
+package com.gree.mall.contest.bean.admin;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class AdminWebsitGrantBean {
+
+    @Schema(description = "角色id")
+    private String adminRoleId;
+    @Schema(description = "功能模块ids")
+    private List<String> adminModuleIds;
+    @Schema(description = "连带的功能模块ids")
+    private List<String> adminModuleIds2 = new ArrayList<>();
+
+}

+ 15 - 0
src/main/java/com/gree/mall/contest/bean/admin/AdminWebsitTree.java

@@ -0,0 +1,15 @@
+package com.gree.mall.contest.bean.admin;
+
+import com.gree.mall.contest.plus.entity.AdminWebsit;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class AdminWebsitTree extends AdminWebsit {
+
+    @Schema(description = "children")
+    private List<AdminWebsitTree> children;
+
+}

+ 11 - 0
src/main/java/com/gree/mall/contest/bean/admin/reqDto/AdminUserAddReqBean.java

@@ -0,0 +1,11 @@
+package com.gree.mall.contest.bean.admin.reqDto;
+
+import com.gree.mall.contest.plus.entity.AdminUser;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AdminUserAddReqBean extends AdminUser {
+
+}

+ 43 - 0
src/main/java/com/gree/mall/contest/bean/common/AmityUrlVO.java

@@ -0,0 +1,43 @@
+package com.gree.mall.contest.bean.common;
+
+
+import com.gree.mall.contest.enums.StatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+
+public class AmityUrlVO {
+
+    @Schema(description = "友情链接id")
+    private String amityUrlId;
+
+    @Schema(description = "友情链接名称")
+    private String amityUrlName;
+
+    @Schema(description = "友链地址")
+    private String amityUrl;
+
+    @Schema(description = "logo")
+    private String amityUrlLogo;
+
+    @Schema(description = "排序")
+    private Integer amityUrlSort;
+
+    @Schema(description = "状态")
+    private StatusEnum status;
+
+    @Schema(description = "创建人")
+    private String createBy;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "更新人")
+    private String updateBy;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+}

+ 52 - 0
src/main/java/com/gree/mall/contest/bean/common/CarouselMapVO.java

@@ -0,0 +1,52 @@
+package com.gree.mall.contest.bean.common;
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.IsYesNoEnum;
+import com.gree.mall.contest.enums.MiniPortEnum;
+import com.gree.mall.contest.enums.StatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ZfireField(tbName = "a")
+public class CarouselMapVO {
+
+    @ZfireField(hide = true)
+    @Schema(description = "轮翻id")
+    private String id;
+
+    @Schema(description = "轮播图名称")
+    private String carouselName;
+
+    @Schema(description = "状态")
+    private StatusEnum state;
+
+    @Schema(description = "小程序端口")
+    private MiniPortEnum port;
+
+    @Schema(description = "是否跳转菜单")
+    private IsYesNoEnum type;
+
+    @Schema(description = "菜单链接")
+    private String linkUrl;
+
+    @Schema(description = "图片地址")
+    private String imgSrc;
+
+    @Schema(description = "排序")
+    private Integer sortNum;
+
+    @Schema(description = "创建人")
+    private String createBy;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "更新人")
+    private String updateBy;
+
+    @Schema(description = "更新时间")
+    private Date updateTime;
+}

+ 41 - 0
src/main/java/com/gree/mall/contest/bean/common/SysDictVO.java

@@ -0,0 +1,41 @@
+package com.gree.mall.contest.bean.common;
+
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.constant.SysDictConstant;
+import com.gree.mall.contest.enums.StateEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@ZfireField(tbName = "a")
+@Data
+public class SysDictVO {
+
+    @Schema(description = "id")
+    @ZfireField(hide = true)
+    private String sysDictId;
+
+    @ZfireField(type="select",frontCode = SysDictConstant.SYS_DICT_TYPE,colName = "dict_type")
+    @Schema(description = "字典类型")
+    private String dictTypeName;
+
+    @ZfireField(type="select")
+    @Schema(description = "状态")
+    private StateEnum status;
+
+    @Schema(description = "字典编号")
+    private String dictCode;
+
+    @Schema(description = "字典值")
+    private String dictValue;
+
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "排序")
+    private Integer sortNum;
+
+
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/bean/common/WechatOpenBean.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.bean.common;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Data
+public class WechatOpenBean {
+    private String openid;
+    private String sessionKey;
+    private String unionid;
+    private String mobile;
+    @Schema(description = "公众号appid")
+    private String pubAppId;
+
+    @Schema(description = "小程序logo")
+    private String logo;
+    @Schema(description = "小程序名称")
+    private String appName;
+}

+ 19 - 0
src/main/java/com/gree/mall/contest/bean/common/WxJsApiSignBean.java

@@ -0,0 +1,19 @@
+package com.gree.mall.contest.bean.common;
+
+import lombok.Data;
+
+/**
+ * @author :lijh
+ * @description:TODO
+ * @date :2024/1/24 19:41
+ */
+@Data
+public class WxJsApiSignBean {
+
+    private String appId;
+    private String nonceStr;
+    private long timestamp;
+    private String url;
+    private String signature;
+
+}

+ 73 - 0
src/main/java/com/gree/mall/contest/bean/manage/NotifyRecordVO.java

@@ -0,0 +1,73 @@
+package com.gree.mall.contest.bean.manage;
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.manage.NotifyTypeEnum;
+import com.gree.mall.contest.enums.manage.SendObjEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ZfireField(tbName = "a")
+public class NotifyRecordVO {
+
+    @ZfireField(hide = true)
+    @Schema(description = "id")
+    private String id;
+
+    @Schema(description = "来源id")
+    private String sourceId;
+
+    @Schema(description = "消息类型")
+    private NotifyTypeEnum type;
+
+    @Schema(description = "标题")
+    private String title;
+
+    @ZfireField(hide = true)
+    @Schema(description = "内容")
+    private String newsContent;
+
+    @Schema(description = "对象")
+    private SendObjEnum sendObj;
+
+    @ZfireField(hide = true)
+    @Schema(description = "是否已读 true:已读,false:未读")
+    private Boolean readFlag;
+
+    @Schema(description = "阅读人")
+    private String readBy;
+
+    @Schema(description = "阅读时间")
+    private Date readTime;
+
+    @ZfireField(hide = true)
+    @Schema(description = "是否确认 true=是 false=否")
+    private Boolean isConfirm;
+
+    @ZfireField(hide = true)
+    @Schema(description = "发布人")
+    private String issueUserId;
+
+    @ZfireField(hide = true)
+    @Schema(description = "发布人昵称")
+    private String issueNickName;
+
+    @ZfireField(hide = true)
+    @Schema(description = "发布时间")
+    private Date issueTime;
+
+    @ZfireField(hide = true)
+    @Schema(description = "接收人")
+    private String receiveId;
+
+    @ZfireField(hide = true)
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @ZfireField(hide = true)
+    @Schema(description = "TAKE_PRICE_CHANGE=收购价格变动 INFO_SETTLE_INVOICE_REMIND=信息费结算账单已开票提醒 QUALITY_WAIT_CONFIRM=报货单质检待确认 BALANCE_REMIND=余额不足 RECHARGE_SUCCESS=充值成功 WITHDRAWAL_SUCCESS=钱包余额提现成功 EXCEPTION_ORDER_APPEAL=异常订单待申诉 EXCEPTION_ORDER_CONFIRM=异常订单待审核 EXCEPTION_ORDER_END=异常订单审核完成 INFO_SETTLE_REMIND=信息费用结算账单待确认提醒 BALANCE_ADD=钱包余额新增(平台确认付款)")
+    private String noticeType;
+
+}

+ 46 - 0
src/main/java/com/gree/mall/contest/bean/manage/NotifyVO.java

@@ -0,0 +1,46 @@
+package com.gree.mall.contest.bean.manage;
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.IsEnum;
+import com.gree.mall.contest.enums.manage.SendObjEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ZfireField(tbName = "a")
+public class NotifyVO {
+
+    @ZfireField(hide = true)
+    @Schema(description = "id")
+    private String id;
+
+    @Schema(description = "标题")
+    private String title;
+
+    @Schema(description = "发送对象")
+    private SendObjEnum sendObj;
+
+    @Schema(description = "是否已发布")
+    private IsEnum isSend;
+
+    @Schema(description = "已读数量")
+    private Integer readCount;
+
+    @Schema(description = "创建人")
+    private String createBy;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "发布人")
+    private String sendBy;
+
+    @Schema(description = "发布时间")
+    private Date sendTime;
+
+    @ZfireField(hide = true)
+    @Schema(description = "状态 SAVE=保存 OK=发布")
+    private String status;
+}

+ 82 - 0
src/main/java/com/gree/mall/contest/bean/manage/UserTopPopVO.java

@@ -0,0 +1,82 @@
+package com.gree.mall.contest.bean.manage;
+
+
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.enums.IsEnum;
+import com.gree.mall.contest.enums.manage.PopJumpTypeEnum;
+import com.gree.mall.contest.enums.manage.PopStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ZfireField(tbName = "a")
+public class UserTopPopVO {
+
+    @ZfireField(hide = true)
+    @Schema(description = "id")
+    private String id;
+
+    @Schema(description = "主标题")
+    private String title;
+
+    @Schema(description = "副标题")
+    private String detailTitle;
+
+    @Schema(description = "跳转链接")
+    private PopJumpTypeEnum jumpType;
+
+    @ZfireField(hide = true)
+    @Schema(description = "内容1 内部页面 详情页面 外部H5 外部小程序")
+    private String content1;
+
+    @ZfireField(hide = true)
+    @Schema(description = "内容2 外部小程序")
+    private String content2;
+
+    @Schema(description = "发送对象")
+    private String sendObj;
+
+    @ZfireField(hide = true)
+    @Schema(description = "发送范围 ALL=全部 APPOINT=部分")
+    private String sendRange;
+
+    @ZfireField(type = "date")
+    @Schema(description = "开始日期")
+    private Date startDate;
+
+    @ZfireField(type = "date")
+    @Schema(description = "结束日期")
+    private Date endDate;
+
+    @Schema(description = "是否已发布")
+    private IsEnum isSend;
+
+    @Schema(description = "状态")
+    private PopStatusEnum status;
+
+    @Schema(description = "创建人")
+    private String createBy;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @ZfireField(hide = true)
+    @Schema(description = "修改人")
+    private String updateBy;
+
+    @ZfireField(hide = true)
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+    @Schema(description = "发布人")
+    private String sendBy;
+
+    @ZfireField(hide = true)
+    @Schema(description = "发布人id")
+    private String sendById;
+
+    @Schema(description = "发布时间")
+    private Date sendTime;
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/bean/zfire/QueryParamBean.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.bean.zfire;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+@Data
+@Accessors(chain = true)
+public class QueryParamBean {
+
+    @Schema(description = "条件名称")
+    private String param;
+    @Schema(description = "条件比较符 ><=like")
+    private String compare;
+    @Schema(description = "条件内容")
+    private Object value;
+    private Date createTime;
+}

+ 25 - 0
src/main/java/com/gree/mall/contest/bean/zfire/ZfireCustomParam.java

@@ -0,0 +1,25 @@
+package com.gree.mall.contest.bean.zfire;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+
+@Data
+public class ZfireCustomParam {
+    @Schema(description = "id")
+    private String id;
+    @NotBlank
+    @Schema(description = "自定义条件名称")
+    private String name;
+    @NotBlank
+    @Schema(description = "模块id")
+    private String moduleId;
+    @NotNull
+    @Schema(description = "组成条件")
+    private List<QueryParamBean> items;
+
+}

+ 99 - 0
src/main/java/com/gree/mall/contest/bean/zfire/ZfireParamBean.java

@@ -0,0 +1,99 @@
+package com.gree.mall.contest.bean.zfire;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.gree.mall.contest.plus.entity.AdminField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+@Data
+public class ZfireParamBean {
+
+    @Schema(description = "页号,不传默认1")
+    private Integer pageNum = 1;
+
+    @Schema(description = "页大小,不传默认10,查全部传 -1")
+    private Integer pageSize = 10;
+
+    @Schema(description = "排序字段例如:table.create_time desc")
+    private String orderBy = "";
+
+    @Schema(description = "查询条件")
+    private List<QueryParamBean> params;
+
+    @Schema(description = "导出的字段,导出必传")
+    private List<AdminField> exportFields;
+
+    @Schema(description = "预留字段(前端忽略该字段)")
+    private String query;
+
+    @Schema(description = "查询项")
+    private String selected;
+
+
+    @Schema(description = "导出用字段")
+    private Integer exportCount = 0;
+
+    @JsonIgnore
+    @Schema(description = "返回类的类型")
+    private Class clazzType;
+
+    @JsonIgnore
+    @Schema(description = "商户字段")
+    private String companyWechatId;
+
+    @JsonIgnore
+    private List<String> adminWebsitIds;
+
+    public Page page(){
+        return new Page(pageNum,pageSize);
+    }
+
+    /**
+     * 查询指定条件内容
+     */
+    public Object getParamValue(String param){
+        if(CollectionUtils.isEmpty(params))
+            return null;
+        List<QueryParamBean> collect = params.stream().filter(v -> v.getParam().equals(param)).collect(Collectors.toList());
+        if(CollectionUtils.isEmpty(collect)){
+            return null;
+        }
+        return collect.get(0).getValue();
+    }
+
+    /**
+     * 指定查询条件内容
+     */
+    public Boolean setParamValue(String param,String compare,Object value){
+        if(CollectionUtils.isEmpty(params)) {
+            params = new ArrayList<>();
+        }
+        //1. if exists remove
+        this.removeParam(param);
+        //2. add
+        QueryParamBean queryParamBean = new QueryParamBean();
+        queryParamBean.setParam(param);
+        queryParamBean.setCompare(compare);
+        queryParamBean.setValue(value);
+        params.add(queryParamBean);
+        return true;
+    }
+
+    /**
+     * 删除指定查询条件
+     */
+    public Boolean removeParam(String param){
+        if(CollectionUtils.isEmpty(params)) {
+            return true;
+        }
+        params.removeIf(v -> v.getParam().equals(param));
+        return true;
+    }
+}

+ 27 - 0
src/main/java/com/gree/mall/contest/commonmapper/AdminMapper.java

@@ -0,0 +1,27 @@
+package com.gree.mall.contest.commonmapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.admin.AdminFastEntryVO;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Update;
+
+@Mapper
+public interface AdminMapper {
+    
+    @Update("update mp_ma_rela a \n" +
+            "join user b on a.union_id= b.union_id \n" +
+            "set \n" +
+            "\ta.ma_user_id = b.user_id,\n" +
+            "\ta.ma_user_name=b.nick_name,\n" +
+            "\ta.ma_phone = b.mobile,\n" +
+            "\tb.mp_open_id = a.mp_open_id\n" +
+            "where a.union_id !='' and a.ma_open_id=''")
+    @InterceptorIgnore(tenantLine = "true")
+    public void updateMpMaRela();
+
+    @InterceptorIgnore(tenantLine = "true")
+    IPage<AdminFastEntryVO> queryAdminFastEntry(Page page, ZfireParamBean zfireParamBean);
+}

+ 62 - 0
src/main/java/com/gree/mall/contest/commonmapper/CommonMapper.java

@@ -0,0 +1,62 @@
+package com.gree.mall.contest.commonmapper;
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.common.AmityUrlVO;
+import com.gree.mall.contest.bean.common.CarouselMapVO;
+import com.gree.mall.contest.bean.manage.NotifyRecordVO;
+import com.gree.mall.contest.bean.manage.NotifyVO;
+import com.gree.mall.contest.bean.manage.UserTopPopVO;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.plus.entity.UserTopPop;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface CommonMapper {
+
+    /**
+     * 友情链接
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<AmityUrlVO> amityList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    /**
+     * 轮播图列表
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<CarouselMapVO> carouselMapList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    /**
+     * 通知公告列表
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<NotifyVO> notifyList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    /**
+     * 通知记录列表
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<NotifyRecordVO> notifyReadList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    /**
+     * 首页弹窗配置列表
+     * @param page
+     * @param zfireParamBean
+     * @return
+     */
+    IPage<UserTopPopVO> topPopList(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    List<UserTopPop> queryTopPopVaildRecord(@Param("curDate") DateTime curDate, @Param("popIds") List<String> popIds);
+}

+ 42 - 0
src/main/java/com/gree/mall/contest/config/BaseEnumConverterFactory.java

@@ -0,0 +1,42 @@
+package com.gree.mall.contest.config;
+
+import com.gree.mall.contest.enums.base.BaseEnum;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author qinrongjun
+ * @description 枚举类型转换工厂
+ * @date 2023/4/6 11:07 星期四
+ */
+public class BaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
+    /**
+     * 缓存BaseEnum实现类
+     */
+    private static final Map<Class<?>, Converter<String, ? extends BaseEnum>> CACHE_MAP = new HashMap<>(32);
+
+    @Override
+    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
+        return (Converter<String, T>) CACHE_MAP.computeIfAbsent(targetType, key -> new BaseEnumImpl<>(targetType));
+    }
+
+    private static class BaseEnumImpl<T extends BaseEnum> implements Converter<String, T> {
+
+        private final Map<String, T> enumMap = new HashMap<>();
+
+        private BaseEnumImpl(Class<T> clazz) {
+            T[] enums = clazz.getEnumConstants();
+            for (T t : enums) {
+                enumMap.put(t.getKey(), t);
+            }
+        }
+
+        @Override
+        public T convert(String source) {
+            return enumMap.get(source);
+        }
+    }
+}

+ 64 - 0
src/main/java/com/gree/mall/contest/config/KaptchaConfig.java

@@ -0,0 +1,64 @@
+package com.gree.mall.contest.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+/**
+ * 验证码
+ */
+
+@Configuration
+public class KaptchaConfig {
+    @Bean
+    public DefaultKaptcha getDefaultKaptcha() {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        properties.setProperty("kaptcha.border", "no");
+        properties.setProperty("kaptcha.textproducer.char.length", "4");
+        properties.setProperty("kaptcha.session.key", "code");
+        properties.setProperty("kaptcha.textproducer.font.color", "black");
+//        properties.setProperty("kaptcha.noise.color", "red");
+        properties.setProperty("kaptcha.textproducer.char.space", "3");
+        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
+        //properties.setProperty("kaptcha.border.color", "105,179,90");
+        //properties.setProperty("kaptcha.image.width", "120");
+        //properties.setProperty("kaptcha.image.height", "45");
+        properties.setProperty("kaptcha.textproducer.font.size", "40");
+        //properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+
+        return defaultKaptcha;
+//        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+//        Properties properties = new Properties();
+//        // 是否有边框 默认为true 我们可以自己设置yes,no
+//        properties.setProperty("kaptcha.border", "yes");
+//        // 边框颜色 默认为Color.BLACK
+//        properties.setProperty("kaptcha.border.color", "105,179,90");
+//        // 验证码文本字符颜色 默认为Color.BLACK
+//        properties.setProperty("kaptcha.textproducer.font.color", "blue");
+//        // 验证码图片宽度 默认为200
+//        properties.setProperty("kaptcha.image.width", "160");
+//        // 验证码图片高度 默认为50
+//        properties.setProperty("kaptcha.image.height", "60");
+//        // 验证码文本字符大小 默认为40
+//        properties.setProperty("kaptcha.textproducer.font.size", "30");
+//        // KAPTCHA_SESSION_KEY
+//        properties.setProperty("kaptcha.session.key", "kaptchaCode");
+//        // 验证码文本字符间距 默认为2
+//        properties.setProperty("kaptcha.textproducer.char.space", "3");
+//        // 验证码文本字符长度 默认为5
+//        properties.setProperty("kaptcha.textproducer.char.length", "5");
+//        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+//        properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
+//        // 验证码噪点颜色 默认为Color.BLACK
+//        properties.setProperty("kaptcha.noise.color", "white");
+//        Config config = new Config(properties);
+//        defaultKaptcha.setConfig(config);
+//        return defaultKaptcha;
+    }
+}

+ 20 - 0
src/main/java/com/gree/mall/contest/config/MybatisPlusConfig.java

@@ -0,0 +1,20 @@
+package com.gree.mall.contest.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisPlusConfig {
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 分页插件
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        return interceptor;
+    }
+}
+

+ 111 - 0
src/main/java/com/gree/mall/contest/config/RedisConfig.java

@@ -0,0 +1,111 @@
+package com.gree.mall.contest.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+import java.time.Duration;
+
+
+@Configuration
+@EnableCaching
+@ConditionalOnClass(RedisOperations.class)
+@EnableConfigurationProperties(RedisProperties.class)
+@Slf4j
+public class RedisConfig {
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        // 使用更安全的类型验证方式
+        om.activateDefaultTyping(
+                LaissezFaireSubTypeValidator.instance,
+                ObjectMapper.DefaultTyping.NON_FINAL,
+                JsonTypeInfo.As.PROPERTY
+        );
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(factory);
+
+        // 统一序列化配置
+        template.setKeySerializer(RedisSerializer.string());
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        template.setHashKeySerializer(RedisSerializer.string());
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+
+        // 移除不必要的afterPropertiesSet调用
+        return template;
+    }
+
+    @Bean
+    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
+        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
+        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
+        //设置默认超过期时间是30秒
+        defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
+        //初始化RedisCacheManager
+        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
+        return cacheManager;
+    }
+
+    @Bean
+    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
+        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        container.setConnectionFactory(connectionFactory);
+       // container.addMessageListener(listenerAdapter, new PatternTopic("__keyevent@0__:expired"));
+        return container;
+    }
+
+
+    /**
+     * 注入封装RedisTemplate
+     * @Title: redisUtil
+     * @return RedisUtil
+     * @throws
+     */
+    @Bean(name = "redisUtil")
+    public RedisUtil redisUtilSingle(RedisTemplate<String, Object> redisTemplate) {
+        RedisUtil redisUtil = new RedisUtil();
+        redisUtil.setRedisTemplate(redisTemplate);
+        return redisUtil;
+    }
+
+    // 使用lambda简化KeyGenerator
+    @Bean
+    public KeyGenerator keyGenerator() {
+        return (target, method, params) -> {
+            StringBuilder sb = new StringBuilder(target.getClass().getName());
+            sb.append(":").append(method.getName());
+            for (Object obj : params) {
+                sb.append(":").append(obj.toString());
+            }
+            return sb.toString();
+        };
+    }
+
+}

+ 17 - 0
src/main/java/com/gree/mall/contest/config/RedisLockConfiguration.java

@@ -0,0 +1,17 @@
+package com.gree.mall.contest.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+
+
+@Configuration
+public class RedisLockConfiguration {
+
+    @Bean
+    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
+        return new RedisLockRegistry(redisConnectionFactory, "spring");
+    }
+
+}

+ 96 - 0
src/main/java/com/gree/mall/contest/config/SwaggerUIConfig.java

@@ -0,0 +1,96 @@
+package com.gree.mall.contest.config;
+
+import org.springdoc.core.GroupedOpenApi;
+import org.springdoc.core.customizers.OpenApiCustomiser;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.awt.*;
+import java.io.IOException;
+import java.net.URI;
+
+@Configuration
+public class SwaggerUIConfig {
+    @Value("${server.port}")
+    private String port;
+    @Value("${spring.profiles.active}")
+    private String active;
+    @Value("${server.servlet.context-path}")
+    private String path;
+    @Bean
+    public OpenApiCustomiser globalHeaderCustomizer() {
+        return openApi -> openApi.getPaths()
+                .values()
+                .forEach(pathItem -> pathItem.readOperations()
+                        .forEach(operation -> operation.addParametersItem(
+                                new io.swagger.v3.oas.models.parameters.Parameter()
+                                        .in("header")
+                                        .name("x-token")
+                                        .required(true)
+                                        .example("1111")
+                                        .description("认证令牌")
+                        )));
+    }
+
+    @Bean
+    public GroupedOpenApi pcApi() {
+        return GroupedOpenApi.builder()
+                .group("管理端接口")
+                .displayName("管理端接口")
+                .pathsToMatch("/pc/**")
+                .build();
+    }
+
+    @Bean
+    public GroupedOpenApi miniApi() {
+        return GroupedOpenApi.builder()
+                .group("移动端接口")
+                .displayName("移动端接口")
+                .pathsToMatch("/mini/**")
+                .build();
+    }
+
+    @Bean
+    public CommandLineRunner openBrowser() {
+        return args -> {
+            try {
+                String swaggerPath = "/doc.html"; // Springdoc路径
+                String url = "http://localhost:" + port + path + swaggerPath;
+                System.out.println("尝试打开Swagger UI: " + url);
+
+                if (Desktop.isDesktopSupported()) {
+                    Desktop desktop = Desktop.getDesktop();
+                    if (desktop.isSupported(Desktop.Action.BROWSE)) {
+                        desktop.browse(new URI(url));
+                        return;
+                    }
+                }
+
+                // Desktop不可用时使用命令行
+                System.out.println("使用命令行方式打开...");
+                openUrlWithCommand(url);
+
+            } catch (Exception e) {
+                System.err.println("无法打开浏览器: " + e.getMessage());
+                e.printStackTrace();
+            }
+        };
+    }
+
+    private void openUrlWithCommand(String url) throws IOException {
+        String os = System.getProperty("os.name").toLowerCase();
+        Runtime runtime = Runtime.getRuntime();
+
+        if (os.contains("win")) {
+            runtime.exec("rundll32 url.dll,FileProtocolHandler " + url);
+        } else if (os.contains("mac")) {
+            runtime.exec("open " + url);
+        } else if (os.contains("nix") || os.contains("nux")) {
+            runtime.exec("xdg-open " + url);
+        } else {
+            System.out.println("请手动访问: " + url);
+        }
+    }
+}

+ 37 - 0
src/main/java/com/gree/mall/contest/config/aop/AopConfig.java

@@ -0,0 +1,37 @@
+package com.gree.mall.contest.config.aop;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.core.annotation.Order;
+
+@Configuration
+@EnableAspectJAutoProxy(
+        proxyTargetClass = true,  // 强制使用CGLIB代理,确保基于类的代理也能正常工作
+        exposeProxy = true        // 暴露代理对象,可通过AopContext获取当前代理
+)
+public class AopConfig {
+    /**
+     * 配置说明:
+     * 1. @EnableAspectJAutoProxy 启用Spring的AOP自动代理功能
+     * 2. proxyTargetClass=true 表示强制使用CGLIB代理:
+     *    - 解决JDK动态代理(基于接口)的局限性
+     *    - 确保没有实现接口的类也能被代理
+     * 3. exposeProxy=true 允许通过AopContext.currentProxy()获取当前代理对象:
+     *    - 解决类内部方法调用不走代理的问题
+     *    - 使用时需通过 AopContext.currentProxy() 调用方法
+     */
+    @Bean
+    @Order(1)
+    public ApiAspect apiAspect() {
+        return new ApiAspect();
+    }
+
+    @Bean
+    @Order(2) // 切面执行顺序,值越小优先级越高
+    public OperationLogAspect operationLogAspect() {
+        return new OperationLogAspect();
+    }
+
+
+}

+ 105 - 0
src/main/java/com/gree/mall/contest/config/aop/ApiAspect.java

@@ -0,0 +1,105 @@
+package com.gree.mall.contest.config.aop;
+
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.plus.service.UserService;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+@Aspect
+@Slf4j
+public class ApiAspect {
+
+    @Value("${spring.profiles.active}")
+    private String profiles;
+    @Autowired
+    RedisUtil redisUtil;
+    @Autowired
+    UserService userService;
+
+    @Pointcut("@annotation(io.swagger.v3.oas.annotations.Operation)")
+    public void auth() {}
+
+    @Before("auth()")
+    public void doBefore(JoinPoint joinPoint) throws RemoteServiceException {
+            start(joinPoint);
+    }
+
+    private void start(JoinPoint joinPoint) throws RemoteServiceException {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        Method sourceMethod = getSourceMethod(joinPoint);
+        HttpServletRequest request = attributes.getRequest();
+        String servletPath = request.getServletPath();
+        ApiNotAuth apiNotAuth = sourceMethod.getAnnotation(ApiNotAuth.class);
+        if(apiNotAuth != null || request.getRequestURI().contains("api-docs")){
+            return;
+        }
+
+        if(!profiles.equals("prd")){
+            if(request.getHeader(Constant.TOKEN_NAME) != null && request.getHeader(Constant.TOKEN_NAME).equals("1111")){
+                return;
+            }
+            if(request.getParameter(Constant.TOKEN_NAME) != null && request.getParameter(Constant.TOKEN_NAME).equals("1111")){
+                return;
+            }
+        }
+        String userId = CommonUtils.getUserId(request);
+        if (StringUtils.isBlank(userId)) {
+            throw new RemoteServiceException(ResponseHelper.ResponseCode_AUTH_ERROR, "请求不合法");
+        }
+        //taijpcapi=pc  zfminiapp = 小程序接口
+        String issuer = CommonUtils.getIssuer(request);
+        if(!servletPath.contains(issuer)){
+            throw new RemoteServiceException("暂无权限访问,请联系相关人员");
+        }
+        //如果是师傅,状态无效,无法访问
+//        String appid = CommonUtils.getAPPID();
+//        if(StringUtils.isNotBlank(appid)) {
+//            AdminCompanyWechat adminCompanyWechat = (AdminCompanyWechat) redisUtil.get(Constant.RedisPrefix.CLIENT + appid);
+//            if (adminCompanyWechat == null) {
+//                throw new RemoteServiceException("非法请求");
+//            }
+//            if (adminCompanyWechat.getFlag() == 2) {
+//                User user = userService.getById(userId);
+//                //还未授权手机号的不用检查
+//                if (StringUtils.isBlank(user.getMobile())) {
+//                    return;
+//                }
+//                Integer count = workerService.lambdaQuery().eq(Worker::getUserId, userId).eq(Worker::getStatus, true).count();
+//                if (count == 0) {
+//                    throw new RemoteServiceException("您暂未入驻任何网点");
+//                }
+//            }
+//        }
+
+    }
+
+    private Method getSourceMethod(JoinPoint jp) {
+        Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod();
+        try {
+            return jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        } catch (SecurityException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 119 - 0
src/main/java/com/gree/mall/contest/config/aop/OperationLogAspect.java

@@ -0,0 +1,119 @@
+package com.gree.mall.contest.config.aop;
+
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.AdminUser;
+import com.gree.mall.contest.plus.entity.OperationLog;
+import com.gree.mall.contest.plus.service.AdminUserService;
+import com.gree.mall.contest.plus.service.OperationLogService;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.IpUtil;
+import com.gree.mall.contest.utils.RedisUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ *  操作日志
+ *  created by lijh
+ *  2020-06-03
+ */
+@Aspect
+@Slf4j
+public class OperationLogAspect {
+
+    @Autowired
+    RedisUtil redisUtil;
+    @Autowired
+    OperationLogService operationLogService;
+    @Autowired
+    AdminUserService adminUserService;
+    @Autowired
+    CommonLogic commonLogic;
+
+    @Pointcut("@annotation(io.swagger.v3.oas.annotations.Operation)")
+    public void auth() {}
+
+    @Before("auth()")
+    public void doBefore(JoinPoint joinPoint){
+        try{
+            start(joinPoint);
+        }catch(Exception e){
+            log.error("操作日志记录失败,{}",e.getMessage());
+        }
+    }
+
+
+    private void start(JoinPoint joinPoint){
+        //不记录的模块
+        List<String> modules = Arrays.asList("小程序API","公共API");
+
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = attributes.getRequest();
+        String adminUserId = CommonUtils.getUserId(request);
+        String nickName = CommonUtils.getRealName(request);
+        Method sourceMethod = getSourceMethod(joinPoint);
+        //只记录post请求的增删改操作
+        PostMapping annotation = sourceMethod.getAnnotation(PostMapping.class);
+        if (annotation != null) {
+            Operation ApiOperation = sourceMethod.getAnnotation(Operation.class);
+            //方法上的注解为具体操作
+            String value = ApiOperation.description();
+            //类上的api注解为模块
+            String moduleName = joinPoint.getTarget().getClass().getAnnotation(Tag.class).name();
+            if(StringUtils.isBlank(moduleName) || modules.contains(moduleName)){
+                return;
+            }
+            //登录接口获取不到token,写死
+            if(value.equals("登录")){
+                String userName = request.getParameter("userName");
+                AdminUser adminUser = adminUserService.lambdaQuery().eq(AdminUser::getUserName, userName).one();
+                nickName = adminUser.getNickName();
+                adminUserId = adminUser.getAdminUserId();
+            }
+            //当前时间
+            Date date = new Date();
+            String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
+
+            //记录
+            OperationLog operationLog = new OperationLog();
+            operationLog.setContent(nickName + "于" + dateStr + value);
+            operationLog.setModuleName(moduleName);
+            operationLog.setAdminUserId(adminUserId);
+            operationLog.setUserName(nickName);
+            operationLog.setNickName(nickName);
+            operationLog.setIp(IpUtil.getIpAddr(request));
+            operationLog.setCreateTime(new Date());
+            operationLogService.save(operationLog);
+        }
+    }
+
+    private Method getSourceMethod(JoinPoint jp) {
+        Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod();
+        try {
+            return jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        } catch (SecurityException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 366 - 0
src/main/java/com/gree/mall/contest/config/aop/ZfireFiledAop.java

@@ -0,0 +1,366 @@
+package com.gree.mall.contest.config.aop;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.common.collect.Maps;
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.plus.entity.AdminField;
+import com.gree.mall.contest.plus.service.AdminFieldService;
+import com.gree.mall.contest.utils.CommonUtils;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.formula.functions.T;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+@Aspect
+@Component
+@Slf4j
+@Order(1)
+public class ZfireFiledAop {
+
+    @Autowired
+    AdminFieldService adminFieldService;
+
+
+    @Pointcut("@annotation(com.gree.mall.contest.annotation.ZfireList)")
+    public void auth() {}
+
+    @AfterReturning(value = "auth()",returning = "result")
+    public void doAfter(JoinPoint joinPoint, Object result) throws Exception {
+        start(joinPoint,result);
+    }
+
+    @Before("auth()")
+    public void doBefore(JoinPoint joinPoint) {
+        Arrays.stream(joinPoint.getArgs())
+                .filter(ZfireParamBean.class::isInstance)
+                .map(ZfireParamBean.class::cast)
+                .findFirst()
+                .ifPresent(item -> buildReturnTypeClazz(item, joinPoint));
+    }
+
+    /**
+     * 解析返回值{@code ResponseHelper<IPage<Obj>>}中的Obj的类型
+     * @param zfireParam
+     * @param joinPoint
+     */
+    private void buildReturnTypeClazz(ZfireParamBean zfireParam, JoinPoint joinPoint) {
+        if (Objects.isNull(zfireParam)) {
+            return;
+        }
+        Signature signature = joinPoint.getSignature();
+        if (!(signature instanceof MethodSignature)) {
+            return;
+        }
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+        Type type = method.getGenericReturnType();
+        if (!(type instanceof ParameterizedType)) {
+            return;
+        }
+        ParameterizedType parameterizedType = (ParameterizedType) type;
+        if (!parameterizedType.getRawType().equals(ResponseHelper.class)) {
+            return;
+        }
+        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+        if (actualTypeArguments.length == 0) {
+            return;
+        }
+        Type actualTypeArgument = actualTypeArguments[0];
+        if (!(actualTypeArgument instanceof ParameterizedType)) {
+            return;
+        }
+        ParameterizedType parameterizedType1 = (ParameterizedType) actualTypeArgument;
+        Type[] actualTypeArgumentsArrays = parameterizedType1.getActualTypeArguments();
+        if (actualTypeArgumentsArrays.length == 0) {
+            return;
+        }
+        Type actualTypeArg = actualTypeArgumentsArrays[0];
+        if (!(actualTypeArg instanceof Class)) {
+            return;
+        }
+        Class<?> clazz = (Class<?>) actualTypeArg;
+        zfireParam.setClazzType(clazz);
+    }
+
+
+    /**
+     * {
+     * 	moduleId:"菜单id"      //不为空
+     * 	jName:"java字段名"     //前端渲染使用的字段名,不为空
+     * 	label:"字段title"      //字段标题
+     * 	placeholder:"提示语"
+     * 	type:"输入类型【input/select】"
+     * 	option:[{
+     * 		value:"",
+     * 		name:""
+     *        }]
+     * 	multiple:true          //true=多选,false=单选
+     * 	frontCode:""           //前端预留code,由前端自定义,默认为空
+     * 	sortNum:1              //排序字段
+     * 	tbName:"表名"          //可能为空(查询组装参数用)
+     * 	colName:"字段名"       //可能为空(查询组装参数用)
+     * }
+     */
+    private void start(JoinPoint joinPoint, Object result) throws Exception {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        //Method sourceMethod = getSourceMethod(joinPoint);
+        HttpServletRequest request = attributes.getRequest();
+        //当前菜单ID
+        String moduleId = request.getParameter("moduleId");
+        //当前用户id
+        String adminUserId = CommonUtils.getUserId(request);
+
+        if(!(result instanceof ResponseHelper)){
+            return;
+        }
+        ResponseHelper responseHelper = (ResponseHelper)result;
+        if(responseHelper.getCode() != ResponseHelper.ResponseCode_Success){
+            return;
+        }
+        Object data = responseHelper.getData();
+        if(!(data instanceof Page)){
+            return;
+        }
+        Page page = (Page) data;
+        List<T> records = page.getRecords();
+        //当前菜单当前用户的配置
+        Map<String, AdminField> fieldMap = adminFieldService.lambdaQuery()
+                .eq(AdminField::getAdminUserId, adminUserId)
+                .eq(AdminField::getModuleId,moduleId)
+                .orderByAsc(AdminField::getSortNum)
+                .list()
+                .stream()
+                .collect(Collectors.toMap(AdminField::getJName, v -> v));
+
+        //返回的内容的对象
+        Object bean = null;
+        if(!CollectionUtils.isEmpty(records)){
+            bean = records.get(0);
+        }else{
+            records = new ArrayList<>();
+            List list1 = this.newInstance(records,responseHelper.typeReference);
+            bean = list1.get(0);
+            // records.getClass().getTypeName();
+        }
+        String typeName = bean.getClass().getTypeName();
+        ZfireField annotation = bean.getClass().getAnnotation(ZfireField.class);
+        String tbName = "";
+        if (Objects.nonNull(annotation)) {
+            tbName = annotation.tbName();
+        }
+
+        List<AdminField> fieldBeans = new ArrayList<>();
+        //反射获取返回对象中的属性
+        Field[] fields = bean.getClass().getDeclaredFields();
+        for(Field field:fields){
+            field.setAccessible(true);
+
+            AdminField fieldBean = this.getTbCol(field, fieldMap, tbName, bean);
+
+            if (Objects.isNull(fieldBean)) {
+                continue;
+            }
+            fieldBeans.add(fieldBean);
+        }
+        //排序
+        fieldBeans = fieldBeans.stream()
+                .sorted(Comparator.comparing(AdminField::getSortNum,Comparator.nullsFirst(Integer::compareTo)))
+                .collect(Collectors.toList());
+        //填充字段集
+        responseHelper.setFieldBeans(fieldBeans);
+    }
+
+    public AdminField getTbCol(Field field, Map<String, AdminField> fieldMap, String classTbName, Object obj) throws NoSuchFieldException, IllegalAccessException {
+        String fieldName = field.getName();
+        //获取属性类型
+        boolean hide;
+//        String typeName = field.getType().getName();
+        AdminField fieldBean = new AdminField();
+        String typeName = field.getType().getTypeName();
+        fieldBean.setJName(fieldName);
+        fieldBean.setColName(xX2x_x(fieldName));
+        //fieldBean.setColName(name);
+        fieldBean.setType("input");
+        fieldBean.setTbName(classTbName);
+        fieldBean.setTiling(true);
+        fieldBean.setIsShow(true);
+        fieldBean.setIsQuery(true);
+        fieldBean.setIsCutting(true);
+
+
+        Annotation[] annotations = field.getAnnotations();
+        for(Annotation annotation : annotations){
+
+            if(annotation instanceof ZfireField){
+                ZfireField zfireField = (ZfireField) annotation;
+                String colName = zfireField.colName();
+                String tbName = zfireField.tbName();
+                String frontCode = zfireField.frontCode();
+                String type = zfireField.type();
+                boolean show = zfireField.isShow();
+                boolean total = zfireField.isTotal();
+                boolean query = zfireField.isQuery();
+                boolean pk = zfireField.pk();
+                hide = zfireField.hide();
+                if (StringUtils.isNotBlank(colName))
+                    fieldBean.setColName(colName);
+
+                if (StringUtils.isNotBlank(tbName))
+                    fieldBean.setTbName(tbName);
+
+                fieldBean.setFrontCode(frontCode);
+                fieldBean.setType(type);
+                fieldBean.setSortNum(zfireField.sortNum());
+                fieldBean.setIsShow(show);
+                fieldBean.setHide(hide);
+                fieldBean.setIsTotal(total);
+                fieldBean.setIsQuery(query);
+                fieldBean.setFixed(zfireField.fixed());
+                fieldBean.setPk(pk);
+                fieldBean.setMultiple(zfireField.multiple());
+                fieldBean.setIsCutting(zfireField.isCutting());
+
+            }else if(annotation instanceof Schema){
+                Schema property = (Schema) annotation;
+                fieldBean.setLabel(property.description());
+            }
+        }
+
+        //处理枚举类型
+        Map<String,String> enumMap = Maps.newHashMap();
+        if(field.getType().isEnum()) {
+            for (Object enumO : field.getType().getEnumConstants()) {
+                Class<?> c = enumO.getClass();
+                if (!BaseEnum.class.isAssignableFrom(c)) {
+                    break;
+                }
+                BaseEnum baseEnum = (BaseEnum)enumO;
+                if (!hasJsonIgnoreAnnotation((Enum<?>) baseEnum)) {
+                    enumMap.put(baseEnum.getKey(), baseEnum.getRemark());
+                }
+            }
+            //枚举类型默认为select
+            fieldBean.setType("select");
+        }
+        fieldBean.setEnumMap(JSONUtil.toJsonStr(enumMap));
+
+        //拿db的配置来覆盖默认的配置
+        if(fieldMap != null) {
+            AdminField adminField = fieldMap.get(fieldName);
+            if (adminField != null) {
+                BeanUtil.copyProperties(adminField, fieldBean, "label","frontCode","type", "hide", "enumMap","isQuery","pk","multiple","jName","colName","tbName","fixed");
+            }
+        }
+
+        if(StringUtils.equals(typeName,"java.math.BigDecimal")){
+            fieldBean.setType("amount");
+        }else if((fieldBean.getType().equals("input") && StringUtils.equals(typeName,"java.util.Date")) || StringUtils.equals(typeName,"java.time.LocalDateTime")){
+            fieldBean.setType("datetime");
+        }else if(StringUtils.equals(typeName,"java.lang.Integer")){
+            fieldBean.setType("number");
+        }
+        if(fieldBean.getSortNum() == null) {
+            fieldBean.setSortNum(999);
+        }
+        return fieldBean;
+    }
+
+    private boolean hasJsonIgnoreAnnotation(Enum<?> enumValue) throws NoSuchFieldException {
+        Field field = enumValue.getClass().getField(enumValue.name());
+        return field.isAnnotationPresent(JsonIgnore.class);
+    }
+
+    private Method getSourceMethod(JoinPoint jp) {
+        Method proxyMethod = ((MethodSignature) jp.getSignature()).getMethod();
+        try {
+            return jp.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        } catch (SecurityException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * @author Howe
+     * @Description 将驼峰转为下划线
+     * @param str
+     * @return java.lang.String
+     * @Date   2022/4/22 13:11
+     * @since  1.0.0
+     */
+    public static String xX2x_x(String str) {
+        Pattern compile = Pattern.compile("[A-Z]");
+        Matcher matcher = compile.matcher(str);
+        StringBuffer sb = new StringBuffer();
+        while(matcher.find()) {
+            matcher.appendReplacement(sb,  "_" + matcher.group(0).toLowerCase());
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+    /**
+     * @author Howe
+     * @Description 将下划线转为驼峰
+     * @param str
+     * @return java.lang.String
+     * @Date   2022/4/22 13:12
+     * @since  1.0.0
+     */
+    public static String x_x2xX(String str) {
+        str = str.toLowerCase();
+        Pattern compile = Pattern.compile("_[a-z]");
+        Matcher matcher = compile.matcher(str);
+        StringBuffer sb = new StringBuffer();
+        while(matcher.find()) {
+            matcher.appendReplacement(sb,  matcher.group(0).toUpperCase().replace("_",""));
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+
+
+    public <T> List newInstance(List<T> list, TypeReference<T> typeReference) throws IllegalAccessException, InstantiationException {
+        Type tp = typeReference.getClass().getGenericSuperclass();
+        Class<T> type = (Class<T>)((ParameterizedType) tp).getActualTypeArguments()[0];
+        T t = type.newInstance();
+        list.add(t);
+        return list;
+    }
+
+}

+ 119 - 0
src/main/java/com/gree/mall/contest/config/wx/WxConfiguration.java

@@ -0,0 +1,119 @@
+package com.gree.mall.contest.config.wx;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.google.common.collect.Maps;
+import com.gree.mall.contest.logic.wxmp.WxMpMessageLogic;
+import com.gree.mall.contest.plus.entity.AdminCompanyWechat;
+import com.gree.mall.contest.plus.service.AdminCompanyWechatService;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpMessageHandler;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+import java.util.Map;
+
+@Component
+@Configuration
+@Slf4j
+public class WxConfiguration {
+
+    private final static String keyPath = "classpath:/static/apiclient_cert.p12";
+
+    //小程序服务
+    public static Map<String, WxMaService> wxMaService = Maps.newConcurrentMap();
+    //微信公众号
+    public static Map<String, WxMpService> wxMpServices = Maps.newConcurrentMap();
+    //微信公众号推送消息
+    public static Map<String, WxMpMessageRouter> wxMpMessageRouters = Maps.newConcurrentMap();
+
+    //    @Value("${wechat.payment.refundNotifyUrl}")
+//    private String refundNotifyUrl;
+    @Autowired
+    RedisUtil redisUtil;
+    @Autowired
+    AdminCompanyWechatService AdminCompanyWechatService;
+
+
+    @PostConstruct
+    public void initWxServices() {
+        //查询企业微信配置
+        List<AdminCompanyWechat> list = AdminCompanyWechatService.list();
+        reloadWxConfig(list);
+    }
+
+    public void reloadWxConfig(List<AdminCompanyWechat> list) {
+
+        for (AdminCompanyWechat companyWechat : list) {
+            if (StringUtils.isBlank(companyWechat.getSubAppId()) || StringUtils.isBlank(companyWechat.getSubSecret())) {
+                continue;
+            }
+            //初始化企业微信服务
+            //initCpService(companyWechat,redisUtil);
+            //初始化微信支付服务
+            //initWxPayService(companyWechat,notifyUrl);
+            //initWxSubPayService(companyWechat,notifyUrl);
+            //初始化微信小程序服务
+            initWxMaService(companyWechat, redisUtil);
+            //初始化微信公众号服务
+            initWxMpService(companyWechat, redisUtil);
+        }
+    }
+
+    /**
+     * 初始化小程序服务
+     */
+    private static void initWxMaService(AdminCompanyWechat companyWechat, RedisUtil redisUtil) {
+        if (StringUtils.isAnyBlank(companyWechat.getSubAppId(), companyWechat.getSubSecret())) {
+            return;
+        }
+        WxMaDefaultConfigImpl config = new WxMaLettuceRedisConfig(redisUtil);
+        // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
+        config.setAppid(companyWechat.getSubAppId());
+        config.setSecret(companyWechat.getSubSecret());
+        WxMaService service = new WxMaServiceImpl();
+        service.setWxMaConfig(config);
+        wxMaService.put(companyWechat.getSubAppId(), service);
+    }
+
+    /**
+     * 初始化微信公众号服务
+     *
+     * @param companyWechat
+     * @param redisUtil
+     */
+    private static void initWxMpService(AdminCompanyWechat companyWechat, RedisUtil redisUtil) {
+        if (StringUtils.isBlank(companyWechat.getPubAppId()) || StringUtils.isBlank(companyWechat.getPubAppSecret())) {
+            return;
+        }
+        log.info("开始初始化微信公众号服务: {}", companyWechat);
+        WxMpService service = new WxMpServiceImpl();
+        WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpLettuceRedisConfig(redisUtil);
+        wxMpDefaultConfig.setAppId(companyWechat.getPubAppId());
+        wxMpDefaultConfig.setSecret(companyWechat.getPubAppSecret());
+        wxMpDefaultConfig.setToken("zfiresxb");
+        wxMpDefaultConfig.setAesKey("2lJ7n6OOZG4xJhCtgPSmM4b8vmOffhIhpwkEJntX151");
+        service.setWxMpConfigStorage(wxMpDefaultConfig);
+
+        // 推送消息处理
+        WxMpMessageHandler textHandler = new WxMpMessageLogic();
+
+        WxMpMessageRouter wxMpMessageRouter = new WxMpMessageRouter(service);
+        wxMpMessageRouter.rule().handler(textHandler).end();
+        wxMpServices.put(companyWechat.getCompanyWechatId(), service);
+        wxMpMessageRouters.put(companyWechat.getCompanyWechatId(), wxMpMessageRouter);
+        log.info("结束初始化微信公众号服务");
+    }
+
+}

+ 63 - 0
src/main/java/com/gree/mall/contest/config/wx/WxMaLettuceRedisConfig.java

@@ -0,0 +1,63 @@
+package com.gree.mall.contest.config.wx;
+
+
+import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.utils.RedisUtil;
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * 重写WxMaService服务,让其支持lettuce客户端
+ */
+public class WxMaLettuceRedisConfig extends WxMaDefaultConfigImpl {
+    private RedisUtil redisUtil;
+    private String accessTokenKey;
+
+    public WxMaLettuceRedisConfig(RedisUtil redisUtil){
+        this.redisUtil = redisUtil;
+    }
+
+    @Override
+    public void setAppid(String appid) {
+        super.setAppid(appid);
+        //this.accessTokenKey = Constant.RedisPrefix.TOKEN_WX+":"+appid;
+        this.accessTokenKey = Constant.RedisPrefix.TOKEN_MP_WX + ":" + appid;
+    }
+
+    @Override
+    public String getAccessToken() {
+        return redisUtil.get(accessTokenKey).toString();
+    }
+
+    @Override
+    public boolean isAccessTokenExpired() {
+        Long expireTime = redisUtil.getExpire(accessTokenKey);
+        if (ObjectUtils.isEmpty(expireTime)) {
+            return true;
+        }
+        // 到期时间小于100秒就算作过期了,就重新调用接口获取
+        return expireTime < 100;
+    }
+
+    @Override
+    public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+        redisUtil.set(accessTokenKey, accessToken, expiresInSeconds - 200);
+    }
+
+    @Override
+    public void expireAccessToken() {
+        redisUtil.expire(accessTokenKey, 0);
+    }
+
+
+    @Override
+    public long getExpiresTime() {
+        Long expire = redisUtil.getExpire(accessTokenKey);
+        return expire == null ? 0 : expire;
+    }
+
+    @Override
+    public void setExpiresTime(long expiresTime) {
+        redisUtil.expire(accessTokenKey, expiresTime);
+    }
+}

+ 67 - 0
src/main/java/com/gree/mall/contest/config/wx/WxMpLettuceRedisConfig.java

@@ -0,0 +1,67 @@
+package com.gree.mall.contest.config.wx;
+
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * 重写WxMpService服务,让其支持lettuce客户端
+ */
+@Slf4j
+public class WxMpLettuceRedisConfig extends WxMpDefaultConfigImpl {
+    private RedisUtil redisUtil;
+    private String accessTokenKey;
+
+    public WxMpLettuceRedisConfig(RedisUtil redisUtil) {
+        this.redisUtil = redisUtil;
+    }
+
+    @Override
+    public void setAppId(String appId) {
+        log.info("重写WxMpService服务 注入appid: {}", appId);
+        super.setAppId(appId);
+        log.info("定义accessTokenKey: {}", Constant.RedisPrefix.TOKEN_MP_WX + ":" + appId);
+        this.accessTokenKey = Constant.RedisPrefix.TOKEN_MP_WX + ":" + appId;
+    }
+
+    @Override
+    public String getAccessToken() {
+        final String token = redisUtil.get(accessTokenKey).toString();
+//        log.info("获取缓存key: {} : {}",  accessTokenKey, token);
+        return token;
+    }
+
+    @Override
+    public boolean isAccessTokenExpired() {
+        Long expireTime = redisUtil.getExpire(accessTokenKey);
+        if (ObjectUtils.isEmpty(expireTime)) {
+            return true;
+        }
+        // 到期时间小于100秒就算作过期了,就重新调用接口获取
+        return expireTime < 100;
+    }
+
+    @Override
+    public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+        redisUtil.set(accessTokenKey, accessToken, expiresInSeconds - 200);
+    }
+
+    @Override
+    public void expireAccessToken() {
+        redisUtil.expire(accessTokenKey, 0);
+    }
+
+
+    @Override
+    public long getExpiresTime() {
+        Long expire = redisUtil.getExpire(accessTokenKey);
+        return expire == null ? 0 : expire;
+    }
+
+    @Override
+    public void setExpiresTime(long expiresTime) {
+        redisUtil.expire(accessTokenKey, expiresTime);
+    }
+}

+ 63 - 0
src/main/java/com/gree/mall/contest/constant/Constant.java

@@ -0,0 +1,63 @@
+package com.gree.mall.contest.constant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Constant {
+
+    //校验的token
+    public static final String TOKEN_NAME = "x-token";
+    //pc端接口前缀
+    public static final String PC_API = "/pc";
+    //小程序端接口前缀
+    public static final String MINIAPP_API = "/miniapp";
+    //默认的租户id
+    public static final String GD_COMPANY_WECHAT_ID = "1";
+
+    // JWT声明常量
+    public static final String CLAIM_USER_ID = "userId";
+    public static final String CLAIM_USERNAME = "username";
+    public static final String CLAIM_REAL_NAME = "realName";
+    public static final String CLAIM_USER_TYPE = "userType";
+    public static final String CLAIM_USER_ROLE = "userRole";
+
+    //系统部门,不可更改
+    public static final List<String> SYS_WEBSIT_NAME = new ArrayList<String>(){{
+        add("平台");
+        add("服务商");
+        add("合作商");
+    }};
+
+    public class SERVICE_PROVIDER{
+        //服务商的父级部门id
+        public final static String PARENT_WEBSIT_ID = "2";
+        //服务商角色id
+        public final static String ROLE_ID = "2";
+        //合作商户角色id
+        public final static String ROLE_MERCHANT_ID = "3";
+
+        public final static String ROLE_SHOPP_ID = "4";
+    }
+
+    public class RedisPrefix {
+        public static final String TOKEN_VERIFICATION = "mall:contest:verification";
+        public static final String TOKEN_MANAGE = "mall:contest:token:manage:";
+        public final static String LOCK_LOGIN = "mall:contest:login:";
+        public final static String WORK_MINI_ACCESS_TOKEN = "mall:contest:work:mini:access:token";
+        public final static String TOKEN_WX = "mall:contest:token:wx";
+        public final static String TOKEN_MP_WX = "mall:contest:mp:token:wx";
+        public final static String SMS = "mall:contest:sms";
+        public final static String VERIFICATION = "mall:contest:verification";
+        public final static String USER_BALANCE = "mall:contest:user:balance:";
+        public final static String OLD_STOCK = "mall:contest:old:stock:";
+        public final static String SERVICE_GENERATE_BILL = "mall:contest:service:generate:bill";
+        public final static String SERVICE2_GENERATE_BILL = "mall:contest:service2:generate:bill";
+        public final static String SERVICE2_PAY_BILL = "mall:contest:service2:pay:bill:";
+        public final static String COUPON_STOCK_LOCK = "mall:contest:COUPON_STOCK_LOCK";
+        public final static String LOCK_ORDER = "mall:contest:lock:order:";
+
+        public final static String CLIENT = "mall:contest:client:";
+    }
+
+    public final static Integer PAGE_SIZE = 100000;
+}

+ 16 - 0
src/main/java/com/gree/mall/contest/constant/SysDictConstant.java

@@ -0,0 +1,16 @@
+package com.gree.mall.contest.constant;
+
+public class SysDictConstant {
+
+
+    //字典类型
+    public final static String SYS_DICT_TYPE="SYS_DICT_TYPE";
+    //结算主体品牌
+    public final static String BRAND="BRAND";
+    //工单渠道
+    public final static String ORDER_CHANNEL = "CHANNEL";
+    //等级
+    public final static String WEBSIT_LEVEL = "websit_level";
+
+
+}

+ 60 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/AdminFastEntryController.java

@@ -0,0 +1,60 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+
+import cn.hutool.core.lang.TypeReference;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.annotation.ZfireList;
+import com.gree.mall.contest.bean.admin.AdminFastEntryVO;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.AdminFastEntryLogic;
+import com.gree.mall.contest.plus.entity.AdminFastEntry;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RequiredArgsConstructor
+@RestController
+@Tag(name = "系统快捷入口", description = "系统快捷入口")
+@RequestMapping("/pc/admin/fast/entry")
+public class AdminFastEntryController {
+
+    private final AdminFastEntryLogic adminFastEntryLogic;
+
+    @ZfireList
+    @PostMapping("list")
+    @Operation(summary = "列表")
+    public ResponseHelper<IPage<AdminFastEntryVO>> list(@RequestBody(required = false) ZfireParamBean zfireParamBean) {
+        IPage<AdminFastEntryVO> adminFastEntryVOIPage = adminFastEntryLogic.listPage(zfireParamBean);
+        return ResponseHelper.success(adminFastEntryVOIPage, new TypeReference<AdminFastEntryVO>() {});
+    }
+
+    @GetMapping("detail")
+    @Operation(summary = "详情")
+    public ResponseHelper<AdminFastEntry> detail(@RequestParam(required = true) String id) {
+        AdminFastEntry detail = adminFastEntryLogic.detail(id);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("add")
+    @Operation(summary = "新增")
+    public ResponseHelper add(@RequestBody AdminFastEntry adminFastEntry) {
+        adminFastEntryLogic.add(adminFastEntry);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("update")
+    @Operation(summary = "修改")
+    public ResponseHelper update(@RequestBody AdminFastEntry adminFastEntry) {
+        adminFastEntryLogic.update(adminFastEntry);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("delete")
+    @Operation(summary = "删除")
+    public ResponseHelper delete(@RequestParam String id) {
+        adminFastEntryLogic.delete(id);
+        return ResponseHelper.success();
+    }
+}

+ 54 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/AdminModuleController.java

@@ -0,0 +1,54 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.gree.mall.contest.bean.admin.AdminModuleBean;
+import com.gree.mall.contest.bean.admin.AdminModuleVO;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.AdminModuleLogic;
+import com.gree.mall.contest.plus.entity.AdminModule;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+@Tag(name = "系统菜单管理", description = "系统菜单管理" )
+@RequestMapping(value = "/pc/admin/module", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class AdminModuleController {
+
+    private final AdminModuleLogic adminModuleLogic;
+
+    @GetMapping("/detail")
+    @Operation(summary = "详情")
+    public ResponseHelper<AdminModuleVO> detail(@RequestParam String moduleId){
+        AdminModuleVO detail = adminModuleLogic.detail(moduleId);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("/add")
+    @Operation(summary = "新增")
+    public ResponseHelper add(@RequestBody AdminModuleBean adminModule){
+        adminModuleLogic.add(adminModule);
+        return ResponseHelper.success();
+    }
+
+
+    @PostMapping("/update")
+    @Operation(summary = "修改")
+    public ResponseHelper update(@RequestBody AdminModule adminModule){
+        adminModuleLogic.update(adminModule);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/delete")
+    @Operation(summary = "删除")
+    public ResponseHelper delete(@RequestParam String id){
+        adminModuleLogic.delete(id);
+        return ResponseHelper.success();
+    }
+
+
+
+}

+ 75 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/AdminRoleController.java

@@ -0,0 +1,75 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.bean.admin.AdminRoleBean;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.AdminRoleLogic;
+import com.gree.mall.contest.plus.entity.AdminRole;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@Tag(name = "系统角色管理", description = "系统角色管理" )
+@RequestMapping(value = "/pc/admin/role", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class AdminRoleController {
+
+
+    private final AdminRoleLogic adminRoleLogic;
+
+    @GetMapping("/list")
+    @Operation(summary = "角色列表")
+    public ResponseHelper<IPage<AdminRoleBean>> list(
+            HttpServletRequest request,
+            @Parameter(description = "部门",required = false) @RequestParam(required = false) String adminWebsitId,
+            @Parameter(description = "页号",required = true) @RequestParam(required = true) Integer pageNum,
+            @Parameter(description = "页大小",required = true) @RequestParam(required = true) Integer pageSize
+    ) throws RemoteServiceException {
+        IPage<AdminRoleBean> adminRoleIPage = adminRoleLogic.listPage(request,adminWebsitId,pageNum, pageSize);
+        return ResponseHelper.success(adminRoleIPage);
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "角色详情")
+    public ResponseHelper<AdminRole> detail(
+            @Parameter(description = "角色id",required = true) @RequestParam(required = true) String adminRoleId
+    ) throws RemoteServiceException {
+        AdminRole detail = adminRoleLogic.detail(adminRoleId);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("/add")
+    @Operation(summary = "新增角色")
+    public ResponseHelper add(
+            HttpServletRequest request,
+            @RequestBody AdminRole adminRole
+    ) throws RemoteServiceException {
+        adminRoleLogic.add(request,adminRole);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/update")
+    @Operation(summary = "修改角色")
+    public ResponseHelper update(@RequestBody AdminRole adminRole) throws RemoteServiceException {
+        adminRoleLogic.update(adminRole);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/delete")
+    @Operation(summary = "删除角色")
+    public ResponseHelper delete(
+            @Parameter(description = "角色id",required = true) @RequestParam String adminRoleId
+    ) throws RemoteServiceException {
+        adminRoleLogic.delete(adminRoleId);
+        return ResponseHelper.success();
+    }
+
+}

+ 233 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/AdminUserController.java

@@ -0,0 +1,233 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.bean.SVerification;
+import com.gree.mall.contest.bean.admin.AdminModuleTree;
+import com.gree.mall.contest.bean.admin.AdminUserBean;
+import com.gree.mall.contest.bean.admin.AdminWebsitGrantBean;
+import com.gree.mall.contest.bean.admin.reqDto.AdminUserAddReqBean;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.AdminUserLogic;
+import com.gree.mall.contest.plus.entity.AdminUser;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+@Slf4j
+@RestController
+@Tag(name = "系统用户管理", description = "后台用户相关API")
+@RequestMapping(value = "/pc/admin/user", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class AdminUserController {
+
+    private final AdminUserLogic adminUserLogic;
+    private final RedisLockRegistry redisLockRegistry;
+
+    @ApiNotAuth
+    @PostMapping("/login")
+    @Operation(summary = "登录")
+    public ResponseHelper<AdminUserBean> login(
+            @Parameter(description = "帐号",required = true) @RequestParam(required = true) String userName,
+            @Parameter(description = "密码",required = false) @RequestParam(required = false) String password,
+            @Parameter(description = "验证码code",required = false) @RequestParam(required = false) String code,
+            @Parameter(description = "验证码值",required = false) @RequestParam(required = false) String codeValue
+
+    ) throws RemoteServiceException, InterruptedException {
+        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_LOGIN + userName);
+        if(!obtain.tryLock(10, TimeUnit.SECONDS)){
+            throw new RemoteServiceException("系统繁忙请稍后再试");
+        }
+        try {
+            AdminUserBean adminUserBean = adminUserLogic.login(userName, password, code, codeValue);
+            return ResponseHelper.success(adminUserBean);
+        }finally {
+            obtain.unlock();
+        }
+    }
+
+    @PostMapping("/password/update")
+    @Operation(summary = "修改密码")
+    public ResponseHelper<AdminUserBean> login(
+            @Parameter(description = "帐号",required = true) @RequestParam(required = true) String userName,
+            @Parameter(description = "密码",required = true) @RequestParam(required = true) String password,
+            @Parameter(description = "新密码",required = true) @RequestParam(required = true) String newPassword
+    ) throws RemoteServiceException {
+        adminUserLogic.updatePassword(userName, password,newPassword);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/status/update")
+    @Operation(summary = "修改帐号状态(冻结/正常)")
+    public ResponseHelper updateStatus(
+            @Parameter(description = "帐号名称",required = true) @RequestParam(required = true) String adminUserId,
+            @Parameter(description = "true:正常  false:冻结",required = true) @RequestParam(required = true) Boolean status
+    ) throws RemoteServiceException {
+        adminUserLogic.updateStatus(adminUserId,status);
+        return ResponseHelper.success();
+    }
+
+
+    @PostMapping("/add")
+    @Operation(summary = "新增帐号")
+    public ResponseHelper add(
+            @Parameter(description = "帐号",required = true) @RequestBody AdminUserAddReqBean adminUserBean
+    ) throws RemoteServiceException {
+        adminUserLogic.add(adminUserBean);
+        return ResponseHelper.success();
+    }
+
+
+    @PostMapping("/user/update")
+    @Operation(summary = "修改帐号")
+    public ResponseHelper updateUser(
+            HttpServletRequest request,
+            @Parameter(description = "帐号", required = true) @RequestBody AdminUserAddReqBean adminUserBean
+    ) throws RemoteServiceException {
+        adminUserLogic.update(request, adminUserBean);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/password/reset")
+    @Operation(summary = "重置密码")
+    public ResponseHelper resetPwd(
+            @Parameter(description = "用户id",required = true) @RequestParam(required = true) String adminUserId,
+            @Parameter(description = "密码",required = true) @RequestParam(required = true) String password,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        adminUserLogic.resetPassword(adminUserId,password,request);
+        return ResponseHelper.success();
+    }
+
+
+    @GetMapping("/list")
+    @Operation(summary = "帐号列表")
+    public ResponseHelper<IPage<AdminUser>> list(
+            @Parameter(description = "部门id",required = false) @RequestParam(required = false) String adminWebsitId,
+            @Parameter(description = "角色id",required = false) @RequestParam(required = false) String roleId,
+            @Parameter(description = "状态 true:正常 false:冻结",required = false) @RequestParam(required = false) Boolean status,
+            @Parameter(description = "用户名",required = false) @RequestParam(required = false) String userName,
+            @Parameter(description = "页号",required = true) @RequestParam(required = true) Integer pageNum,
+            @Parameter(description = "页大小",required = true) @RequestParam(required = true) Integer pageSize,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        IPage<AdminUser> page = adminUserLogic.list(adminWebsitId,roleId,status,userName,pageNum, pageSize);
+        return ResponseHelper.success(page);
+    }
+
+    @GetMapping("/module/list")
+    @Operation(summary = "功能菜单权限列表")
+    public ResponseHelper<List<AdminModuleTree>> moduleList(
+            @Parameter(description = "用户id",required = true) @RequestParam(required = true) String adminUserId
+    ) throws RemoteServiceException {
+        List<AdminModuleTree> adminModuleTrees = adminUserLogic.queryAdminModule(adminUserId);
+        return ResponseHelper.success(adminModuleTrees);
+    }
+
+    @GetMapping("/module/id/checked")
+    @Operation(summary = "查询选中的功能模块ids")
+    public ResponseHelper<List<String>> queryModuleIdChecked(
+            @Parameter(description = "角色id",required = true) @RequestParam(required = true) String adminRoleId
+    ) throws RemoteServiceException {
+        List<String> moduleIds = adminUserLogic.queryModuleIdChecked(adminRoleId);
+        return ResponseHelper.success(moduleIds);
+    }
+
+    @GetMapping("/module/all")
+    @Operation(summary = "全部功能菜单权限列表")
+    public ResponseHelper<List<AdminModuleTree>> allModuleList(
+            @Parameter(description = "角色",required = true) @RequestParam(required = true) String adminRoleId,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        List<AdminModuleTree> adminModuleTrees = adminUserLogic.queryAllAdminModuleTree(adminRoleId,request);
+        return ResponseHelper.success(adminModuleTrees);
+    }
+
+    @PostMapping("/module/grant")
+    @Operation(summary = "授权动能菜单权限")
+    public ResponseHelper grant(
+            @RequestBody AdminWebsitGrantBean adminWebsitGrantBean
+            ) throws RemoteServiceException {
+        adminUserLogic.grantModules(adminWebsitGrantBean);
+        return ResponseHelper.success();
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "详情")
+    public ResponseHelper<AdminUserBean> detail(
+            @Parameter(description = "帐号id",required = true) @RequestParam(required = true) String adminUserId
+    ) throws RemoteServiceException {
+        AdminUserBean detail = adminUserLogic.detail(adminUserId);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("/logout")
+    @Operation(summary = "退出登录")
+    public ResponseHelper logout(HttpServletRequest request){
+        adminUserLogic.logout(request);
+        return ResponseHelper.success();
+    }
+
+    @ApiNotAuth
+    @GetMapping("/imageVerification")
+    @Operation(summary = "获取图片验证码")
+    public ResponseHelper<SVerification> defaultKaptcha()
+            throws Exception {
+        SVerification sVerification = adminUserLogic.defaultKaptcha();
+        return ResponseHelper.success(sVerification);
+    }
+
+    @ApiNotAuth
+    @PostMapping("/reset/password/getCode")
+    @Operation(summary = "找回密码-获取验证码")
+    public ResponseHelper<AdminUserBean> resetPasswordGetCode (
+            @Parameter(description = "账号",required = true) @RequestParam(required = true) String userName,
+            @Parameter(description = "手机号",required = true) @RequestParam String mobile,
+            @Parameter(description = "短信验证码",required = true) @RequestParam String code
+    ) throws RemoteServiceException {
+        AdminUserBean bean = adminUserLogic.resetPasswordGetCode(userName,mobile, code);
+        return ResponseHelper.success(bean);
+    }
+
+    @ApiNotAuth
+    @PostMapping("/reset/password/update")
+    @Operation(summary = "找回密码-更新密码")
+    public ResponseHelper resetPasswordUpdate (
+            @Parameter(description = "系统用户id",required = true) @RequestParam String adminUserId,
+            @Parameter(description = "token",required = true) @RequestParam String token,
+            @Parameter(description = "新密码",required = true) @RequestParam String newPassword,
+            @Parameter(description = "确认密码",required = true) @RequestParam String confirmPassword
+    ) throws RemoteServiceException {
+        adminUserLogic.resetPasswordUpdate(adminUserId, token, newPassword, confirmPassword);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/service/update/mp/mobile")
+    @Operation(summary = "服务商-更新公众号接收手机号")
+    public ResponseHelper serviceUpdateMpMobile (
+            @Parameter(description = "手机号",required = true) @RequestParam String mobile
+    ) throws RemoteServiceException {
+        adminUserLogic.serviceUpdateMpMobile(mobile);
+        return ResponseHelper.success();
+    }
+
+    @Operation(summary = "续期token")
+    @PostMapping("/renewal")
+    public ResponseHelper<AdminUserBean> renewal(
+            @RequestParam String token
+    ) throws RemoteServiceException {
+        AdminUserBean adminUserBean = adminUserLogic.renewal(token);
+        return ResponseHelper.success(adminUserBean);
+    }
+}

+ 107 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/AdminWebsitController.java

@@ -0,0 +1,107 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.gree.mall.contest.bean.admin.AdminWebsitTree;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.AdminWebsitLogic;
+import com.gree.mall.contest.plus.entity.AdminWebsit;
+import com.gree.mall.contest.plus.entity.AdminWebsitRegion;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Slf4j
+@RestController
+@Tag(name = "部门管理API", description = "部门管理API")
+@RequestMapping(value = "/pc/admin/websit", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class AdminWebsitController {
+
+    private final AdminWebsitLogic adminWebsitLogic;
+
+    @GetMapping("/tree")
+    @Operation(summary = "部门树")
+    public ResponseHelper<AdminWebsitTree> tree(HttpServletRequest request,
+                                                @Parameter(description = "角色查询",required = false)
+                                                @RequestParam(required = false,defaultValue = "false") Boolean isRole
+                                                ){
+        List<AdminWebsitTree> tree = adminWebsitLogic.tree(request,isRole);
+        return ResponseHelper.success(tree);
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "部门列表")
+    public ResponseHelper<List<AdminWebsit>> list(
+            @RequestParam(required = false) Boolean isAll,
+            @RequestParam(required = false) Boolean status
+    ){
+        List<AdminWebsit> list = adminWebsitLogic.list(isAll,status);
+        return ResponseHelper.success(list);
+    }
+
+
+    @PostMapping("/add")
+    @Operation(summary = "新增部门")
+    public ResponseHelper add(@RequestBody AdminWebsit adminWebsit, HttpServletRequest request) throws RemoteServiceException, WxErrorException {
+        adminWebsitLogic.add(adminWebsit,request);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/update")
+    @Operation(summary = "修改部门")
+    public ResponseHelper update(@RequestBody AdminWebsit adminWebsit,HttpServletRequest request) throws RemoteServiceException, WxErrorException {
+        adminWebsitLogic.update(adminWebsit,request);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/delete")
+    @Operation(summary = "删除部门")
+    public ResponseHelper delete(@RequestParam String id, HttpServletRequest request) throws WxErrorException {
+        adminWebsitLogic.delete(id,request);
+        return ResponseHelper.success();
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "部门详情")
+    public ResponseHelper<AdminWebsit> detail(@RequestParam String id){
+        AdminWebsit detail = adminWebsitLogic.detail(id);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("/region/save")
+    @Operation(summary = "保存部门服务区域")
+    public ResponseHelper<List<AdminWebsitRegion>> saveRegion(
+            @RequestBody List<AdminWebsitRegion> adminWebsitRegions
+    ){
+        adminWebsitLogic.saveRegion(adminWebsitRegions);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/region/list")
+    @Operation(summary = "部门服务区域列表")
+    public ResponseHelper<List<AdminWebsitRegion>> listRegion(
+            @RequestParam String adminWebsitId,
+            @Parameter(description = "关键词",required = false) @RequestParam(required = false) String keyword,
+            @Parameter(description = "服务类目id",required = false) @RequestParam(required = false) String serviceCategoryId
+    ){
+        List<AdminWebsitRegion> adminWebsitRegions = adminWebsitLogic.listRegion(adminWebsitId,keyword,serviceCategoryId);
+        return ResponseHelper.success(adminWebsitRegions);
+    }
+
+    @PostMapping("/region/delete")
+    @Operation(summary = "删除部门服务区域")
+    public ResponseHelper deleteRegion(
+            @RequestParam String id
+    ){
+        adminWebsitLogic.deleteRegion(id);
+        return ResponseHelper.success();
+    }
+
+}

+ 61 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/OperationLogController.java

@@ -0,0 +1,61 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.OperationLogLogic;
+import com.gree.mall.contest.plus.entity.OperationLog;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Slf4j
+@RestController
+@Tag(name = "操作日志", description = "操作日志" )
+@RequestMapping(value = "/pc/admin/operation/log", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class OperationLogController {
+    
+    private final OperationLogLogic operationLogLogic;
+
+    @GetMapping("/list")
+    @Operation(summary = "列表")
+    public ResponseHelper<IPage<OperationLog>> list(
+            HttpServletRequest request,
+            @Parameter(description = "模块名称",required = false) @RequestParam(required = false) String moduleName,
+            @Parameter(description  = "商户名称",required = false) @RequestParam(required = false) String companyWechatName,
+            @Parameter(description  = "账号昵称",required = false) @RequestParam(required = false) String nickName,
+            @Parameter(description  = "开始时间",required = false) @RequestParam(required = false) String startTime,
+            @Parameter(description  = "结束时间",required = false) @RequestParam(required = false) String endTime,
+            @Parameter(description  = "页号",required = true) @RequestParam(required = true) Integer pageNo,
+            @Parameter(description  = "页大小",required = true) @RequestParam(required = true) Integer pageSize
+    ) throws RemoteServiceException {
+        IPage<OperationLog> operationLogIPage = operationLogLogic.listPage(request, moduleName,companyWechatName,nickName, startTime, endTime, pageNo, pageSize);
+        return ResponseHelper.success(operationLogIPage);
+    }
+
+    @GetMapping("/module")
+    @Operation(summary = "所属模块")
+    public ResponseHelper<List<String>> moduleList(HttpServletRequest request){
+        return ResponseHelper.success(operationLogLogic.moduleList(request));
+    }
+
+
+    @GetMapping("/detail")
+    @Operation(summary = "详情")
+    public ResponseHelper<OperationLog> detail(
+            @Parameter(description  = "operationLogId",required = true) @RequestParam(required = true) String operationLogId
+    ) throws RemoteServiceException {
+        OperationLog detail = operationLogLogic.detail(operationLogId);
+        return ResponseHelper.success(detail);
+    }
+}

+ 47 - 0
src/main/java/com/gree/mall/contest/controller/pc/admin/SysConfigController.java

@@ -0,0 +1,47 @@
+package com.gree.mall.contest.controller.pc.admin;
+
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.admin.SysConfigLogic;
+import com.gree.mall.contest.plus.entity.SysConfig;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RequiredArgsConstructor
+@Slf4j
+@RestController
+@Tag(name = "系统应用配置API", description = "系统应用配置API")
+@RequestMapping(value = "/pc/sys/config", produces = "application/json; charset=utf-8")
+public class SysConfigController {
+
+    private final SysConfigLogic sysConfigLogic;
+
+    @PostMapping("/save")
+    @Operation(summary = "保存系统应用配置")
+    public ResponseHelper save(
+            @RequestBody(required = true)SysConfig sysConfig
+    ) throws RemoteServiceException {
+        sysConfigLogic.save(sysConfig);
+        return ResponseHelper.success();
+    }
+
+
+    @PostMapping("/detail")
+    @Operation(summary = "详情")
+    public ResponseHelper<SysConfig> detail() throws RemoteServiceException {
+        SysConfig detail = sysConfigLogic.detail();
+        return ResponseHelper.success(detail);
+    }
+
+
+
+
+
+}

+ 166 - 0
src/main/java/com/gree/mall/contest/controller/pc/common/CommonController.java

@@ -0,0 +1,166 @@
+package com.gree.mall.contest.controller.pc.common;
+
+import com.google.common.collect.Lists;
+import com.gree.mall.contest.annotation.ApiNotAuth;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.SMSLogic;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.logic.common.WechatLogic;
+import com.gree.mall.contest.plus.entity.AdminCompanyWechat;
+import com.gree.mall.contest.plus.entity.CommonFile;
+import com.gree.mall.contest.plus.entity.Region;
+import com.gree.mall.contest.plus.service.AdminCompanyWechatService;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.RedisUtil;
+import com.gree.mall.contest.utils.VerifiUtils;
+import com.gree.mall.contest.utils.oss.OSSUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotBlank;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+
+@Slf4j
+@RestController
+@Tag(name = "公共API", description = "公共API" )
+@RequestMapping(value = "/pc/common", produces = "application/json; charset=utf-8")
+@RequiredArgsConstructor
+public class CommonController {
+
+    private final CommonLogic commonLogic;
+    private final WechatLogic wechatLogic;
+    private final AdminCompanyWechatService adminCompanyWechatService;
+    private final OSSUtil ossUtil;
+    private final SMSLogic smsLogic;
+    private final RedisUtil redisUtil;
+
+    @PostMapping("/upload")
+    @Operation(summary = "文件上传")
+    public ResponseHelper<CommonFile> upload(
+            @Parameter(required = true, description = "附件") @RequestParam(required = true) MultipartFile file
+    ) throws IOException, RemoteServiceException {
+        CommonFile commonFile = commonLogic.uploadFile(file);
+        return ResponseHelper.success(commonFile);
+    }
+
+
+    @ApiNotAuth
+    @GetMapping("/region/list")
+    @Operation(summary = "地区列表")
+    public ResponseHelper<List<Region>> regionList(
+            @Parameter(description = "父id(第一级传0)",required = true) @RequestParam(required = true) String pid
+    ) throws RemoteServiceException {
+        List<Region> list = commonLogic.queryRegionList(Lists.newArrayList(pid));
+        return ResponseHelper.success(list);
+    }
+
+
+    @PostMapping("/region/list2")
+    @Operation(summary = "地区列表2")
+    public ResponseHelper<List<Region>> regionList(
+            @RequestBody(required = true) List<String> pid
+    ) throws RemoteServiceException {
+        List<Region> list = commonLogic.queryRegionList(pid);
+        return ResponseHelper.success(list);
+    }
+
+
+    @PostMapping("/companywechat/get")
+    @Operation(summary = "获取商户配置二维码头像等详情")
+    public ResponseHelper<AdminCompanyWechat> getCompanyWechat(){
+        AdminCompanyWechat one = adminCompanyWechatService.lambdaQuery()
+                .eq(AdminCompanyWechat::getCompanyWechatId, "1").one();
+        return ResponseHelper.success(one);
+    }
+
+    @GetMapping("template/download")
+    @Operation(summary = "下载模板")
+    public void download(@RequestParam(required = false) @NotBlank(message = "需下载的模板名称不能为空") String name, HttpServletResponse response) throws IOException {
+        CommonUtils.downloadFile(name, response);
+    }
+
+    @ApiNotAuth
+    @Operation(summary = "获取获取前往小程序链接")
+    @GetMapping("/getUrlScheme")
+    public ResponseHelper<String> getUrlScheme(
+            @Parameter(description = "通过 scheme 码进入小程序时的 query",required = false) @RequestParam(required = false) String query,
+            @Parameter(required = false) @RequestParam(required = false) String companyWechatId
+    ) throws IOException, WxErrorException {
+        return ResponseHelper.success(wechatLogic.getUrlScheme(query,companyWechatId));
+    }
+
+    @ApiNotAuth
+    @GetMapping("/img/get")
+    @Operation(summary = "获取图片")
+    public void getImg(
+            @Parameter(required = true, description = "附件key") @RequestParam(required = true) String key,
+            HttpServletResponse response
+    ) throws IOException, RemoteServiceException {
+        String file = commonLogic.getFile(key);
+        response.sendRedirect(file);
+    }
+
+
+    @ApiNotAuth
+    @GetMapping("/file/get")
+    @Operation(summary = "获取文件")
+    public ResponseHelper<String> getFile(
+            @Parameter(required = true, description = "附件key") @RequestParam(required = true) String key,
+            HttpServletResponse response
+    ) throws IOException, RemoteServiceException {
+        String filepath = ossUtil.getUrlWw(key);
+        return ResponseHelper.success(filepath);
+    }
+
+
+    @ApiNotAuth
+    @GetMapping("oss/config")
+    @Operation(summary = "获取oss配置")
+    public ResponseHelper<Map<String, String>> getOssConfig() throws UnsupportedEncodingException {
+        return ResponseHelper.success(commonLogic.getOSSConfig());
+    }
+
+
+    @GetMapping("/getVerifi")
+    @Operation(summary = "获取拖拽式验证码")
+    public ResponseHelper<Map<String, Object>> getVerifi(){
+        Map<String, Object> map = VerifiUtils.getVerifi();
+        Integer xWidth = (Integer) map.get("xWidth");
+        String key = UUID.randomUUID().toString();
+        redisUtil.set(Constant.RedisPrefix.VERIFICATION + ":" + key,xWidth,2*60);
+        map.remove("xWidth");
+        map.put("key",key);
+        return ResponseHelper.success(map);
+    }
+
+
+
+    @ApiNotAuth
+    @PostMapping("/sms/send")
+    @Operation(summary = "发送验证码(绑定手机号专用)")
+    public ResponseHelper sendSms(
+            @Parameter(description = "手机号码",required = true) @RequestParam String phone,
+            @Parameter(description = "滑动验证key",required = false) @RequestParam(required = false) String key,
+            @Parameter(description = "滑动验证数值",required = false) @RequestParam(required = false) String vrifyCode,
+            @Parameter(description = "发送类型 BIND=绑定手机号 RESET=找回密码", required = false) @RequestParam(required = false, defaultValue = "BIND") String sendType
+    ) throws Exception {
+
+        smsLogic.sendSms(phone,key,vrifyCode, sendType);
+        return ResponseHelper.success();
+    }
+
+}

+ 115 - 0
src/main/java/com/gree/mall/contest/controller/pc/zfire/ZfireController.java

@@ -0,0 +1,115 @@
+package com.gree.mall.contest.controller.pc.zfire;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.zfire.QueryParamBean;
+import com.gree.mall.contest.bean.zfire.ZfireCustomParam;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.AdminField;
+import com.gree.mall.contest.plus.entity.AdminFieldCustomParam;
+import com.gree.mall.contest.plus.entity.AdminFieldCustomParamItem;
+import com.gree.mall.contest.plus.service.AdminFieldCustomParamItemService;
+import com.gree.mall.contest.plus.service.AdminFieldCustomParamService;
+import com.gree.mall.contest.plus.service.AdminFieldService;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/pc/zfire")
+@Tag(name = "通用列表API", description = "通用列表API")
+@RequiredArgsConstructor
+public class ZfireController {
+
+    private final CommonLogic commonLogic;
+    private final AdminFieldService adminFieldService;
+    private final AdminFieldCustomParamService adminFieldCustomParamService;
+    private final AdminFieldCustomParamItemService adminFieldCustomParamItemService;
+
+    @PostMapping("save")
+    @Schema(description = "保存配置的列表字段")
+    public ResponseHelper<List<AdminField>> save(@RequestBody List<AdminField> adminFieldList) {
+        if (CollectionUtils.isEmpty(adminFieldList)) {
+            return ResponseHelper.success();
+        }
+        String adminUserId = adminFieldList.get(0).getAdminUserId();
+        String moduleId = adminFieldList.get(0).getModuleId();
+        adminFieldService.lambdaUpdate().eq(AdminField::getAdminUserId, adminUserId).eq(AdminField::getModuleId, moduleId).remove();
+        adminFieldService.saveBatch(adminFieldList);
+        return ResponseHelper.success(adminFieldList);
+    }
+
+    @PostMapping("delete")
+    @Schema(description = "删除配置的列表字段")
+    public ResponseHelper save(@RequestParam String adminUserId, @RequestParam String moduleId) {
+        adminFieldService.lambdaUpdate().eq(AdminField::getAdminUserId, adminUserId).eq(AdminField::getModuleId, moduleId).remove();
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("param/save")
+    @Schema(description = "自定义的查询条件-保存")
+    public ResponseHelper saveParam(@Valid @RequestBody ZfireCustomParam zfireCustomParam) {
+        if (CollectionUtils.isEmpty(zfireCustomParam.getItems())) {
+            throw new RemoteServiceException("缺少必要的参数items");
+        }
+        if (zfireCustomParam.getName().length() > 8) {
+            throw new RemoteServiceException("自定义查询方案名称长度不可超过8");
+        }
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+        if (StringUtils.isBlank(zfireCustomParam.getId())) {
+            Long count = adminFieldCustomParamService.lambdaQuery()
+                    .eq(AdminFieldCustomParam::getAdminUserId, adminUser.getAdminUserId())
+                    .eq(AdminFieldCustomParam::getModuleId, zfireCustomParam.getModuleId())
+                    .count();
+            if (count >= 10) {
+                throw new RemoteServiceException("最多支持10个自定义查询方案");
+            }
+        }
+        AdminFieldCustomParam adminFieldCustomParam = new AdminFieldCustomParam();
+        adminFieldCustomParam.setId(zfireCustomParam.getId());
+        adminFieldCustomParam.setName(zfireCustomParam.getName());
+        adminFieldCustomParam.setModuleId(zfireCustomParam.getModuleId());
+        adminFieldCustomParam.setAdminUserId(adminUser.getAdminUserId());
+        adminFieldCustomParamService.saveOrUpdate(adminFieldCustomParam);
+        adminFieldCustomParamItemService.lambdaUpdate().eq(AdminFieldCustomParamItem::getAdminFieldCustomId, adminFieldCustomParam.getId()).remove();
+        List<AdminFieldCustomParamItem> items = BeanUtil.copyToList(zfireCustomParam.getItems(), AdminFieldCustomParamItem.class);
+        items.forEach(v -> v.setAdminFieldCustomId(adminFieldCustomParam.getId()));
+        adminFieldCustomParamItemService.saveBatch(items);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("param/delete")
+    @Schema(description = "自定义的查询条件-删除")
+    public ResponseHelper deleteParam(@RequestParam String id) {
+        adminFieldCustomParamItemService.lambdaUpdate().eq(AdminFieldCustomParamItem::getAdminFieldCustomId, id).remove();
+        adminFieldCustomParamService.removeById(id);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("param/list")
+    @Schema(description = "自定义的查询条件-查询")
+    public ResponseHelper<List<ZfireCustomParam>> listParam(@RequestParam String moduleId) {
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+        List<AdminFieldCustomParam> list = adminFieldCustomParamService.lambdaQuery()
+                .eq(AdminFieldCustomParam::getAdminUserId, adminUser.getAdminUserId())
+                .eq(AdminFieldCustomParam::getModuleId, moduleId).list();
+        List<ZfireCustomParam> zfireCustomBeans = BeanUtil.copyToList(list, ZfireCustomParam.class);
+        for (ZfireCustomParam bean : zfireCustomBeans) {
+            List<AdminFieldCustomParamItem> items = adminFieldCustomParamItemService.lambdaQuery().eq(AdminFieldCustomParamItem::getAdminFieldCustomId, bean.getId()).list();
+            List<QueryParamBean> newItems = BeanUtil.copyToList(items, QueryParamBean.class);
+            bean.setItems(newItems);
+        }
+        return ResponseHelper.success(zfireCustomBeans);
+    }
+
+}

+ 46 - 0
src/main/java/com/gree/mall/contest/convert/BaseEnumDeserializer.java

@@ -0,0 +1,46 @@
+package com.gree.mall.contest.convert;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.gree.mall.contest.enums.base.BaseEnum;
+
+import java.io.IOException;
+
+/**
+ * @author qinrongjun
+ * @description
+ * @date 2023/8/31 15:08 星期四
+ */
+public class BaseEnumDeserializer extends JsonDeserializer<BaseEnum> implements ContextualDeserializer {
+
+    // 记录枚举字段的类,用于获取其定义的所有枚举值
+    private Class<? extends BaseEnum> propertyClass;
+
+    public BaseEnumDeserializer() {
+    }
+
+    public BaseEnumDeserializer(Class<? extends BaseEnum> propertyClass) {
+        this.propertyClass = propertyClass;
+    }
+
+    @Override
+    public BaseEnum deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
+        String key = p.getValueAsString();
+        return BaseEnum.keyToEnum(propertyClass, key);
+    }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctx, BeanProperty property) {
+        if (property == null) {
+            return new BaseEnumDeserializer((Class<? extends BaseEnum>) ctx.getContextualType().getRawClass());
+        }
+        if (property.getType().isCollectionLikeType()) {
+            return new BaseEnumDeserializer((Class<? extends BaseEnum>) property.getType().containedType(0).getRawClass());
+        }
+        return new BaseEnumDeserializer((Class<? extends BaseEnum>) property.getType().getRawClass());
+    }
+
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/enums/IsEnum.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.IEnum;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum IsEnum implements BaseEnum, IEnum<Boolean> {
+    Y("true", "是"),
+    N("false", "否"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+
+    @Override
+    public Boolean getValue() {
+        return Boolean.parseBoolean(key);
+    }
+}

+ 22 - 0
src/main/java/com/gree/mall/contest/enums/IsYesNoEnum.java

@@ -0,0 +1,22 @@
+package com.gree.mall.contest.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum IsYesNoEnum implements BaseEnum {
+    Y("YES","是"),
+    N("NO","否"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+}

+ 22 - 0
src/main/java/com/gree/mall/contest/enums/MiniPortEnum.java

@@ -0,0 +1,22 @@
+package com.gree.mall.contest.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum MiniPortEnum implements BaseEnum {
+    USER("USER","用户端"),
+    WORKER("WORKER","师傅端"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/enums/StateEnum.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.enums;
+
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum StateEnum implements BaseEnum {
+    ON("ON","有效"),
+    OFF("OFF","无效");
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+
+    private final String remark;
+
+    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+    public static StateEnum create(String key) {
+        return BaseEnum.keyToEnum(StateEnum.class, key);
+    }
+}

+ 28 - 0
src/main/java/com/gree/mall/contest/enums/StatusEnum.java

@@ -0,0 +1,28 @@
+package com.gree.mall.contest.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.IEnum;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum StatusEnum implements BaseEnum, IEnum<Boolean> {
+    ONE("true", "有效"),
+    TWO("false", "无效"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+
+    @Override
+    public Boolean getValue() {
+        return Boolean.parseBoolean(key);
+    }
+}

+ 24 - 0
src/main/java/com/gree/mall/contest/enums/admin/AdminWebsitTypeEnum.java

@@ -0,0 +1,24 @@
+package com.gree.mall.contest.enums.admin;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum AdminWebsitTypeEnum implements BaseEnum {
+    A("A","平台"),
+    B("B","商户网点"),
+    C("C","普通网点")
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+
+
+}

+ 32 - 0
src/main/java/com/gree/mall/contest/enums/admin/BalanceOrderTypeEnum.java

@@ -0,0 +1,32 @@
+package com.gree.mall.contest.enums.admin;
+
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 交易类型
+ */
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum BalanceOrderTypeEnum implements BaseEnum {
+    CHARGE("CHARGE","充值"),
+    WITHDRAWAL("WITHDRAWAL","提现"),
+    ORDER_OVER("ORDER_OVER","工单完工"),
+    ORDER_REJECT("ORDER_REJECT","工单驳回"),
+    MERCHANT_SETTLE("MERCHANT_SETTLE","商家结算"),
+    HAND_FEE("HAND_FEE","手续费"),
+    INFO_FEE("INFO_FEE","信息费"),
+    PERK_FEE("PERK_FEE","用户提现优惠券"),
+    USER_WITHDRAWAL_FEE("USER_WITHDRAWAL_FEE","用户提现"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+}

+ 71 - 0
src/main/java/com/gree/mall/contest/enums/admin/RoleTypeEnum.java

@@ -0,0 +1,71 @@
+package com.gree.mall.contest.enums.admin;
+
+
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.Getter;
+
+@Getter
+public enum RoleTypeEnum implements BaseEnum {
+    A("A","平台"),
+    B("B","服务商"),
+    C("C","合作商"),
+    D("D","服务商子账号"),
+    E("E","合作商子账号"),
+    F("F","推单员")
+    ;
+
+    private String code;
+    private String msg;
+
+    RoleTypeEnum(String code, String msg){
+        this.code=code;
+        this.msg=msg;
+    }
+
+    /**
+     * 是否为平台
+     * @param type
+     * @return
+     */
+    public static Boolean isPlatform(String type){
+        return type.equals(RoleTypeEnum.A.getCode());
+    }
+
+
+    /**
+     * 是否为服务商
+     * @param type
+     * @return
+     */
+    public static Boolean isServiceProvider(String type){
+        return type.equals(RoleTypeEnum.B.getCode()) || type.equals(RoleTypeEnum.D.getCode());
+    }
+
+    /**
+     * 是否为合作商
+     * @param type
+     * @return
+     */
+    public static Boolean isMerchant(String type){
+        return type.equals(RoleTypeEnum.C.getCode()) || type.equals(RoleTypeEnum.E.getCode());
+    }
+
+    /**
+     * 是否为推单员
+     * @param type
+     * @return
+     */
+    public static Boolean isShop(String type){
+        return type.equals(RoleTypeEnum.F.getCode());
+    }
+
+    @Override
+    public String getKey() {
+        return this.code;
+    }
+
+    @Override
+    public String getRemark() {
+        return this.msg;
+    }
+}

+ 30 - 0
src/main/java/com/gree/mall/contest/enums/admin/UpdateLogRemarkEnum.java

@@ -0,0 +1,30 @@
+package com.gree.mall.contest.enums.admin;
+
+
+import lombok.Getter;
+
+@Getter
+public enum UpdateLogRemarkEnum {
+    A("服务商管理"),
+    B("服务商合同管理"),
+    C("服务商保证金管理"),
+    D("工程师管理"),
+    E("合作商管理"),
+    F("合作商合同管理"),
+    G("合作商保证金管理"),
+    H("推单员管理"),
+    I("拆解厂档案管理"),
+    J("回收品类配置管理"),
+    K("计价方式"),
+    L("回收品类配置-服务商"),
+    M("收购参考价"),
+    ;
+
+    private String remark;
+
+    UpdateLogRemarkEnum(String remark){
+        this.remark = remark;
+    }
+
+
+}

+ 222 - 0
src/main/java/com/gree/mall/contest/enums/base/BaseEnum.java

@@ -0,0 +1,222 @@
+package com.gree.mall.contest.enums.base;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.mapstruct.Named;
+import org.mapstruct.TargetType;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * @description: 枚举基类
+ * @author: qinrongjun
+ * @date: 2023/3/10 15:34
+ **/
+@JsonDeserialize(using = com.gree.mall.contest.enums.base.BaseEnumDeserializer.class)
+public interface BaseEnum {
+
+    @Schema(description = "标识")
+    String getKey();
+
+    @Schema(description = "标识中文")
+    String getRemark();
+
+
+    @JsonIgnore
+    default Integer getIntKey() {
+        return Optional.ofNullable(getKey())
+                .filter(NumberUtils::isCreatable)
+                .map(Integer::valueOf)
+                .orElse(null);
+    }
+
+    /**
+     * key转换为枚举,不存在抛出异常
+     *
+     * @param clazz 枚举类型
+     * @param key   枚举的key字段值
+     * @return
+     */
+    @Named("keyToEnumNotNull")
+    static <T extends BaseEnum> T keyToEnumNotNull(@TargetType Class<T> clazz, String key) {
+        return keyToEnumOpt(clazz, key)
+                .orElseThrow(() -> new RemoteServiceException(String.format("枚举值[%s]不存在", key)));
+    }
+
+    /**
+     * int类型key转换为枚举,不存在抛出异常
+     *
+     * @param clazz
+     * @param key
+     * @param <T>
+     * @return
+     */
+    @Named("intKeyToEnumNotNull")
+    static <T extends BaseEnum> T intKeyToEnumNotNull(@TargetType Class<T> clazz, Integer key) {
+        return intKeyToEnumOpt(clazz, key)
+                .orElseThrow(() -> new RemoteServiceException(String.format("枚举值[%s]不存在", key)));
+
+    }
+
+
+    /**
+     * 通用转换对应的枚举实例
+     *
+     * @param clazz 枚举类型
+     * @param key   枚举的key字段值
+     * @return
+     */
+    @Named("keyToEnum")
+    static <T extends BaseEnum> T keyToEnum(@TargetType Class<T> clazz, String key) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getKey().equals(key))
+                .findAny()
+                .orElse(null);
+
+    }
+
+    @Named("intKeyToEnum")
+    static <T extends BaseEnum> T intKeyToEnum(@TargetType Class<T> clazz, Integer key) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getIntKey().equals(key))
+                .findAny()
+                .orElse(null);
+
+    }
+
+    @Named("intKeyToEnumOpt")
+    static <T extends BaseEnum> Optional<T> intKeyToEnumOpt(@TargetType Class<T> clazz, Integer key) {
+        return Optional.ofNullable(intKeyToEnum(clazz, key));
+    }
+
+    @Named("intKeyToValue")
+    static String intKeyToValue(@TargetType Class<? extends BaseEnum> clazz, Integer key) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getIntKey().equals(key))
+                .findAny()
+                .map(BaseEnum::getRemark)
+                .orElse(null);
+
+    }
+
+    @Named("intKeyToValueOpt")
+    static Optional<String> intKeyToValueOpt(@TargetType Class<? extends BaseEnum> clazz, Integer key) {
+        return Optional.ofNullable(intKeyToValue(clazz, key));
+
+    }
+
+    @Named("keyToValue")
+    static String keyToValue(@TargetType Class<? extends BaseEnum> clazz, String key) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getKey().equals(key))
+                .findAny()
+                .map(BaseEnum::getRemark)
+                .orElse(null);
+
+    }
+
+    @Named("keyToValueOpt")
+    static Optional<String> keyToValueOpt(@TargetType Class<? extends BaseEnum> clazz, String key) {
+        return Optional.ofNullable(keyToValue(clazz, key));
+
+    }
+
+    @Named("keyToEnumOpt")
+    static <T extends BaseEnum> Optional<T> keyToEnumOpt(@TargetType Class<T> clazz, String key) {
+        return Optional.ofNullable(keyToEnum(clazz, key));
+    }
+
+    @Named("enumToKey")
+    static String enumToKey(BaseEnum baseEnum) {
+        if (baseEnum == null) {
+            return null;
+        }
+        return baseEnum.getKey();
+    }
+
+    @Named("enumToValue")
+    static String enumToValue(BaseEnum baseEnum) {
+        if (baseEnum == null) {
+            return null;
+        }
+        return baseEnum.getRemark();
+    }
+
+    @Named("enumToIntKey")
+    static Integer enumToIntKey(BaseEnum baseEnum) {
+        if (baseEnum == null) {
+            return null;
+        }
+        return baseEnum.getIntKey();
+    }
+
+    @Named("valueToEnum")
+    static <T extends BaseEnum> T valueToEnum(@TargetType Class<T> clazz, String value) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getRemark().equals(value))
+                .findAny()
+                .orElse(null);
+
+    }
+
+    @Named("valueToEnum")
+    static <T extends BaseEnum> T valueToEnumDefault(@TargetType Class<T> clazz, String value, BaseEnum defaultEnum) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return (T) defaultEnum;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getRemark().equals(value))
+                .findAny()
+                .orElse((T) defaultEnum);
+
+    }
+
+    @Named("valueToKey")
+    static String valueToKey(@TargetType Class<? extends BaseEnum> clazz, String value) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getRemark().equals(value))
+                .findAny()
+                .map(BaseEnum::getKey)
+                .orElse(null);
+
+    }
+
+    @Named("valueToIntKey")
+    static Integer valueToIntKey(@TargetType Class<? extends BaseEnum> clazz, String value) {
+        if (!clazz.isEnum() | !BaseEnum.class.isAssignableFrom(clazz)) {
+            return null;
+        }
+        return Arrays.stream(clazz.getEnumConstants())
+                .filter(item -> item.getRemark().equals(value))
+                .findAny()
+                .map(BaseEnum::getIntKey)
+                .orElse(null);
+
+    }
+
+    static <T extends BaseEnum> Optional<T> valueToEnumOpt(@TargetType Class<T> clazz, String value) {
+        return Optional.ofNullable(valueToEnum(clazz, value));
+    }
+}

+ 45 - 0
src/main/java/com/gree/mall/contest/enums/base/BaseEnumDeserializer.java

@@ -0,0 +1,45 @@
+package com.gree.mall.contest.enums.base;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+
+import java.io.IOException;
+
+/**
+ * @author qinrongjun
+ * @description
+ * @date 2023/8/31 15:08 星期四
+ */
+public class BaseEnumDeserializer extends JsonDeserializer<BaseEnum> implements ContextualDeserializer {
+
+    // 记录枚举字段的类,用于获取其定义的所有枚举值
+    private Class<? extends BaseEnum> propertyClass;
+
+    public BaseEnumDeserializer() {
+    }
+
+    public BaseEnumDeserializer(Class<? extends BaseEnum> propertyClass) {
+        this.propertyClass = propertyClass;
+    }
+
+    @Override
+    public BaseEnum deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
+        String key = p.getValueAsString();
+        return BaseEnum.keyToEnum(propertyClass, key);
+    }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctx, BeanProperty property) {
+        if (property == null) {
+            return new BaseEnumDeserializer((Class<? extends BaseEnum>) ctx.getContextualType().getRawClass());
+        }
+        if (property.getType().isCollectionLikeType()) {
+            return new BaseEnumDeserializer((Class<? extends BaseEnum>) property.getType().containedType(0).getRawClass());
+        }
+        return new BaseEnumDeserializer((Class<? extends BaseEnum>) property.getType().getRawClass());
+    }
+
+}

+ 38 - 0
src/main/java/com/gree/mall/contest/enums/manage/NoticeTypeEnum.java

@@ -0,0 +1,38 @@
+package com.gree.mall.contest.enums.manage;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum NoticeTypeEnum implements BaseEnum {
+
+    TAKE_PRICE_CHANGE("TAKE_PRICE_CHANGE","收购价格变动"),
+    INFO_SETTLE_INVOICE_REMIND("INFO_SETTLE_INVOICE_REMIND","信息费结算账单已开票提醒"),
+    FEE_SETTLE_INVOICE_REMIND("FEE_SETTLE_INVOICE_REMIND","信息费结算账单已开票提醒"),
+    QUALITY_WAIT_CONFIRM("QUALITY_WAIT_CONFIRM","报货单质检待确认"),
+    BALANCE_REMIND("BALANCE_REMIND","余额不足"),
+    RECHARGE_SUCCESS("RECHARGE_SUCCESS","充值成功"),
+    WITHDRAWAL_SUCCESS("WITHDRAWAL_SUCCESS","钱包余额提现成功"),
+    EXCEPTION_ORDER_APPEAL("EXCEPTION_ORDER_APPEAL","异常订单待申诉"),
+    EXCEPTION_ORDER_CONFIRM("EXCEPTION_ORDER_CONFIRM", "异常订单待审核"),
+    EXCEPTION_ORDER_END("EXCEPTION_ORDER_END", "异常订单审核完成"),
+    INFO_SETTLE_REMIND("INFO_SETTLE_REMIND", "信息费用结算账单待确认提醒"),
+    BALANCE_ADD("BALANCE_ADD", "钱包余额新增(平台确认付款)"),
+    ORDER_REMIND("ORDER_REMIND", "订单提醒"),
+    ACTIVITY_REMIND("ACTIVITY_REMIND", "活动提醒"),
+    ACCOUNT_BALANCE_CHANGE("ACCOUNT_BALANCE_CHANGE", "账户余额变动"),
+    MINI_SYS_NEWS("MINI_SYS_NEWS", "小程序系统消息"),
+    WORKER_CERT_EXPIRE("WORKER_CERT_EXPIRE", "师傅证件即将过期"),
+    WORKER_CERT_TIMEOUT("WORKER_CERT_TIMEOUT", "师傅证件已过期"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+}

+ 21 - 0
src/main/java/com/gree/mall/contest/enums/manage/NotifyTypeEnum.java

@@ -0,0 +1,21 @@
+package com.gree.mall.contest.enums.manage;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum NotifyTypeEnum implements BaseEnum {
+    NOTIFY("NOTIFY","通知公告"),
+    NEWS("NEWS","消息"),
+            ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+}

+ 23 - 0
src/main/java/com/gree/mall/contest/enums/manage/PopJumpTypeEnum.java

@@ -0,0 +1,23 @@
+package com.gree.mall.contest.enums.manage;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum PopJumpTypeEnum implements BaseEnum {
+    INNER("INNER","内部页面"),
+    OUT_H5("OUT_H5","外部H5"),
+    OUT_MI("OUT_MI","外部小程序"),
+    DETAIL("DETAIL","详情页面"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+}

+ 19 - 0
src/main/java/com/gree/mall/contest/enums/manage/PopStatusEnum.java

@@ -0,0 +1,19 @@
+package com.gree.mall.contest.enums.manage;
+
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.Getter;
+
+@Getter
+public enum PopStatusEnum implements BaseEnum {
+    SAVE("SAVE","保存"),
+    OK("OK","发布"),
+    CANCEL("CANCEL","取消"),
+    EXPIRE("EXPIRE","已过期"),
+    ;
+
+    PopStatusEnum(String key, String remark) {
+        this.key=key;this.remark = remark;
+    }
+    private String key;
+    private String remark;
+}

+ 25 - 0
src/main/java/com/gree/mall/contest/enums/manage/SendObjEnum.java

@@ -0,0 +1,25 @@
+package com.gree.mall.contest.enums.manage;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.contest.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+public enum SendObjEnum implements BaseEnum {
+    A("A","平台"),
+    S("S","服务商"),
+    M("M","合作商家"),
+    C("C","用户端"),
+    W("W","工程师端"),
+    P("P","推单端"),
+    ;
+
+    @EnumValue
+    @JsonValue
+    private final String key;
+    private final String remark;
+}

+ 38 - 0
src/main/java/com/gree/mall/contest/exception/RemoteServiceException.java

@@ -0,0 +1,38 @@
+package com.gree.mall.contest.exception;
+
+
+import com.gree.mall.contest.helper.ResponseHelper;
+
+public class RemoteServiceException extends RuntimeException {
+    private int status;
+    private String message;
+
+    public RemoteServiceException(String message){
+        this.status = ResponseHelper.ResponseCode_COMMON;
+        this.message = message;
+    }
+
+    public RemoteServiceException(int status, String message) {
+
+        this.status = status;
+        this.message = message;
+
+    }
+
+    @Override
+    public String getMessage() {
+        return this.message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public int getCode() {
+        return this.status;
+    }
+
+    public ResponseHelper toResponse(){
+        return ResponseHelper.error(this.status,this.message);
+    }
+}

+ 97 - 0
src/main/java/com/gree/mall/contest/helper/ResponseHelper.java

@@ -0,0 +1,97 @@
+package com.gree.mall.contest.helper;
+
+import cn.hutool.core.lang.TypeReference;
+import com.gree.mall.contest.plus.entity.AdminField;
+
+import java.util.List;
+
+public class ResponseHelper<T> {
+
+
+    public static int ResponseCode_Success = 200;
+    public static int ResponseCode_COMMON = 501;//通用拦截提示
+    public static int ResponseCode_AUTH_ERROR = 1001;//非法请求
+    public static final int ResponseCode_VALIDATION_ERROR = 4005;// 校验拦截提示
+    public static final int ResponseCode_STOP_SERVICE = 4004;// 校验拦截提示
+
+    private T data;
+    private String message;
+    private int code;
+    public TypeReference<T> typeReference;
+
+    private List<AdminField> fieldBeans;
+
+
+    public static ResponseHelper success() {
+        ResponseHelper object = new ResponseHelper(ResponseCode_Success, "success");
+        return object;
+    }
+
+    public static ResponseHelper success(Object data) {
+        ResponseHelper object = new ResponseHelper(ResponseCode_Success, "success");
+        object.setData(data);
+        return object;
+    }
+
+    public static ResponseHelper success(int code,Object data,String message) {
+        ResponseHelper object = new ResponseHelper(ResponseCode_Success, "success");
+        object.setData(data);
+        object.setCode(code);
+        object.setMessage(message);
+        return object;
+    }
+    public static <T> ResponseHelper success(Object data,TypeReference<T> typeReference) {
+        ResponseHelper object = new ResponseHelper(ResponseCode_Success, "success");
+        object.setData(data);
+        object.typeReference = typeReference;
+        return object;
+    }
+
+
+    public static ResponseHelper error(String msg) {
+        ResponseHelper object = new ResponseHelper(ResponseCode_COMMON, msg);
+        return object;
+    }
+
+    public static ResponseHelper error(int code, String msg) {
+        ResponseHelper object = new ResponseHelper(code, msg);
+        return object;
+    }
+    public void setFieldBeans(List<AdminField> fieldBeans) {
+        this.fieldBeans = fieldBeans;
+    }
+
+    public List<AdminField> getFieldBeans() {
+        return this.fieldBeans;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = (T)data;
+    }
+
+    public ResponseHelper(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int status) {
+        this.code = status;
+    }
+
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

+ 212 - 0
src/main/java/com/gree/mall/contest/logic/SMSLogic.java

@@ -0,0 +1,212 @@
+package com.gree.mall.contest.logic;
+
+import cn.hutool.json.JSONUtil;
+import com.aliyun.oss.ClientException;
+import com.aliyuncs.CommonRequest;
+import com.aliyuncs.CommonResponse;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.profile.DefaultProfile;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.plus.entity.SmsRecord;
+import com.gree.mall.contest.plus.service.SmsRecordService;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SMSLogic {
+
+    private final RedisUtil redisUtil;
+    private final SmsRecordService smsRecordService;
+
+    //原格匠工单系统的
+//    private final static String TEMPLATE_SMS_CODE = "SMS_212320165";
+//    private final static String accessKeyId = "LTAI4GK1q4mnpCFbonMd1pji";
+//    private final static String accessKeySecret = "E5LW0V1H8HBxqjKkExIxaXUgSyex6C";
+//    private final static String SIGN_NAME = "格力客服中心";
+
+    private final static String accessKeyId = "LTAI5tDHiEGKuwzNhW2gLPyc";
+    private final static String accessKeySecret = "NVXSx3Gj7fHv6unGwWgalJh49uMZOI";
+    //private final static String SIGN_NAME = "众炬网络";
+    private final static String SIGN_NAME = "售修宝";
+    //短信验证码
+    private final static String TEMPLATE_SMS_CODE = "SMS_465295106";
+
+
+    //优惠申请通知
+    private final static String TEMPLATE_SMS_DISCOUNT = "SMS_252710269";
+
+
+//    @Test
+//    public void test() throws Exception {
+//        sendDiscountNotice("13533031787","1234567890",  new BigDecimal(10),new BigDecimal(1),"ljh","ljh2");
+//    }
+
+
+    /**
+     * 发送优惠申请通知
+     * @param mobile 手机号
+     * @param orderNo  订单号
+     * @param totalAmount 当前金额
+     * @param discunt 优惠金额
+     * @param name 申请人
+     * @param user 收货人
+     */
+    public void sendDiscountNotice(String mobile, String orderNo, BigDecimal totalAmount,BigDecimal discunt,String name,String user){
+        try {
+            String orderNo2 = orderNo.substring(orderNo.length() - 6, orderNo.length());
+            Map<String, Object> map = new HashMap<>();
+            map.put("orderno", orderNo2);
+            map.put("totalmount", totalAmount);
+            map.put("discunt", discunt);
+            map.put("name", name);
+            map.put("user", user);
+            this.send(mobile, this.TEMPLATE_SMS_DISCOUNT, map);
+        }catch(Exception e){
+            log.error("发送优惠申请通知失败",e);
+        }
+    }
+
+
+    /**
+     * 发送短信验证码
+     * @param mobile 手机号
+     * @param key 拖拽式验证码的key
+     * @param vrifyCode 拖拽式验证码的x轴值
+     * @param sendType 发送短信类型
+     * @throws Exception
+     */
+    public void sendSms(String mobile, String key, String vrifyCode, String sendType) throws Exception {
+
+        if (mobile.equals("13000000000")) {
+            return;
+        }
+        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(vrifyCode)) {
+            boolean b = this.checkVrifyCode(key, vrifyCode);
+            if (!b) {
+                throw new RemoteServiceException("验证码不通过");
+            }
+        }
+        //限制不能频繁发送
+        this.checkSendTime(mobile);
+
+        //6位随机数
+        Random random = new Random();
+        int num = (int) (random.nextDouble() * (1000000 - 100000) + 100000);
+        String code = String.format("%06d", num);
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("code", code);
+        String content = this.send(mobile, TEMPLATE_SMS_CODE, map);
+
+        //记录短信内容
+        SmsRecord smsRecord = new SmsRecord();
+        smsRecord.setPhone(mobile);
+        smsRecord.setContent(content);
+        smsRecord.setCreateTime(new Date());
+        smsRecordService.save(smsRecord);
+
+        //记录发送的code
+        redisUtil.set(Constant.RedisPrefix.SMS + ":" + sendType + ":" + mobile,code,5*60);
+    }
+
+
+    /**
+     * 校验短信验证码的正确性
+     */
+    public void checkSmsCode(String mobile, String code, String sendType) throws RemoteServiceException {
+        String redisKey = Constant.RedisPrefix.SMS + ":" + sendType + ":" + mobile;
+        String value = (String)redisUtil.get(redisKey);
+        if(!StringUtils.equals(value,code)){
+            throw new RemoteServiceException("短信验证失败");
+        }
+        redisUtil.del(redisKey);
+    }
+
+
+    /**
+     * 验证手机号码两次发送的间隔,不能太短
+     *
+     * @param mobile 手机号码
+     * @throws RemoteServiceException
+     */
+    private void checkSendTime(String mobile) throws RemoteServiceException {
+        String key = Constant.RedisPrefix.SMS + ":" + mobile;
+        if(redisUtil.hasKey(key)){
+            throw new RemoteServiceException("发送短信间隔时间太短,请耐心等候");
+        }
+    }
+
+
+    /**
+     * 校验验证码
+     *
+     * @param key
+     * @param vrifyCode
+     * @return
+     */
+    private boolean checkVrifyCode(String key, String vrifyCode) throws RemoteServiceException {
+        Object serverCode = redisUtil.get(Constant.RedisPrefix.VERIFICATION +":"+key);
+        if (serverCode == null) {
+            throw new RemoteServiceException("验证码超时");
+        }
+        //允许拖拽式验证码误差+-10
+        int code = (int) serverCode;
+        int code2 = Integer.parseInt(vrifyCode);
+        if (code >= (code2 - 15) && code <= (code2 + 15)) {
+            redisUtil.del(Constant.RedisPrefix.VERIFICATION +":"+key);
+            return true;
+        } else {
+            throw new RemoteServiceException("验证码校验不通过");
+        }
+    }
+
+
+
+
+    private String send(String mobile,String templateCode,Map<String,Object> param) throws ClientException, com.aliyuncs.exceptions.ClientException {
+
+        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
+        IAcsClient client = new DefaultAcsClient(profile);
+
+        CommonRequest request = new CommonRequest();
+        request.setMethod(MethodType.POST);
+        request.setDomain("dysmsapi.aliyuncs.com");
+        request.setVersion("2017-05-25");
+        request.setAction("SendSms");
+        request.putQueryParameter("RegionId", "cn-hangzhou");
+        request.putQueryParameter("PhoneNumbers", mobile);
+        request.putQueryParameter("SignName", SIGN_NAME);  //需要在平台上添加
+        request.putQueryParameter("TemplateCode", templateCode);  //模版id 需要在拼台上添加
+
+        String content = JSONUtil.toJsonStr(param);
+        log.info("短信发送的内容" + content + mobile);
+        request.putQueryParameter("TemplateParam", content);
+        CommonResponse response = client.getCommonResponse(request);
+        //打印返回数据
+        log.info("短信验证返回数据 :" + response.getData());
+
+        String dateRes = response.getData();
+        Map<String,Object> smsRsp = JSONUtil.parseObj(dateRes);
+        //获取数据
+        String codeRes = smsRsp.get("Code").toString();
+        if(!codeRes.equals("OK")) {
+            log.error("短信发送失败,mobile:{}",mobile);
+        }
+        return content;
+    }
+
+
+}

+ 78 - 0
src/main/java/com/gree/mall/contest/logic/admin/AdminFastEntryLogic.java

@@ -0,0 +1,78 @@
+package com.gree.mall.contest.logic.admin;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.admin.AdminFastEntryVO;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.zfire.QueryParamBean;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.commonmapper.AdminMapper;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.AdminFastEntry;
+import com.gree.mall.contest.plus.service.AdminFastEntryService;
+import com.gree.mall.contest.utils.zfire.FieldUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class AdminFastEntryLogic {
+
+    private final AdminMapper adminMapper;
+    private final AdminFastEntryService adminFastEntryService;
+    private final CommonLogic commonLogic;
+
+    /**
+     * 列表
+     */
+    public IPage<AdminFastEntryVO> listPage(ZfireParamBean zfireParamBean) {
+        final AdminUserCom adminUser = commonLogic.getAdminUser();
+        if (CollectionUtil.isEmpty(zfireParamBean.getParams())) {
+            zfireParamBean.setParams(new ArrayList<>());
+        }
+        QueryParamBean paramBean = new QueryParamBean();
+        paramBean.setParam("a.admin_user_id")
+                .setCompare("=")
+                .setValue(adminUser.getAdminUserId());
+        zfireParamBean.getParams().add(paramBean);
+        zfireParamBean = FieldUtils.supplyParam(zfireParamBean);
+        return adminMapper.queryAdminFastEntry(new Page(zfireParamBean.getPageNum(),zfireParamBean.getPageSize()),zfireParamBean);
+    }
+
+    /**
+     * 新增
+     */
+    public void add(AdminFastEntry adminFastEntry) {
+        final AdminUserCom adminUser = commonLogic.getAdminUser();
+        adminFastEntry.setAdminUserId(adminUser.getAdminUserId());
+        adminFastEntryService.save(adminFastEntry);
+    }
+
+    /**
+     * 修改角色
+     */
+    @Transactional
+    public void update(AdminFastEntry adminFastEntry) {
+        adminFastEntryService.updateById(adminFastEntry);
+    }
+
+    /**
+     * 删除角色
+     */
+    public void delete(String id) {
+        adminFastEntryService.removeById(id);
+    }
+
+    /**
+     * 详情
+     */
+    public AdminFastEntry detail(String id) {
+        return adminFastEntryService.getById(id);
+    }
+}

+ 100 - 0
src/main/java/com/gree/mall/contest/logic/admin/AdminModuleLogic.java

@@ -0,0 +1,100 @@
+package com.gree.mall.contest.logic.admin;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.gree.mall.contest.bean.admin.AdminModuleBean;
+import com.gree.mall.contest.bean.admin.AdminModuleVO;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.plus.entity.AdminModule;
+import com.gree.mall.contest.plus.service.AdminModuleService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class AdminModuleLogic {
+
+    private final AdminModuleService adminModuleService;
+
+
+    @Transactional
+    public void add(AdminModuleBean adminModule){
+        Integer level = 1;
+        if(!adminModule.getParentId().equals("0")){
+            AdminModule parent = adminModuleService.getById(adminModule.getParentId());
+            if(parent == null){
+                throw new RemoteServiceException("请选择正确的父级菜单");
+            }
+            level = parent.getLevel() + 1;
+            parent.updateById();
+        }
+        Long count = adminModuleService.lambdaQuery()
+                .eq(AdminModule::getCode,adminModule.getCode())
+                .eq(AdminModule::getParentId,adminModule.getParentId())
+                .count();
+        if(count > 0){
+            throw new RemoteServiceException("code或者url重复");
+        }
+        adminModule.setLevel(level);
+        adminModule.setCreateTime(new Date());
+        adminModule.insert();
+        //添加子集
+        if(CollectionUtils.isNotEmpty(adminModule.getChildList())){
+            for (AdminModule module : adminModule.getChildList()) {
+                module.setLevel(adminModule.getLevel() + 1);
+                module.setParentId(adminModule.getModuleId());
+            }
+            adminModuleService.saveBatch(adminModule.getChildList());
+        }
+    }
+
+
+    public void update(AdminModule adminModule){
+        Integer level = 1;
+        if(adminModule.getParentId() != null && !adminModule.getParentId().equals("0")){
+            AdminModule parent = adminModuleService.getById(adminModule.getParentId());
+            if(parent == null){
+                throw new RemoteServiceException("请选择正确的父级菜单");
+            }
+            level = parent.getLevel() + 1;
+        }
+        Long count = adminModuleService.lambdaQuery()
+                .ne(AdminModule::getModuleId,adminModule.getModuleId())
+                .eq(AdminModule::getCode,adminModule.getCode())
+                .eq(AdminModule::getParentId,adminModule.getParentId())
+                .count();
+        if(count > 0){
+            throw new RemoteServiceException("code或者url重复");
+        }
+        if(adminModule.getType() != 3) {
+            adminModule.setLevel(level);
+        }
+        adminModule.updateById();
+    }
+
+
+    public void delete(String id){
+        adminModuleService.removeById(id);
+    }
+
+
+    public AdminModuleVO detail(String id){
+        AdminModule adminModule = adminModuleService.getById(id);
+        AdminModuleVO adminModuleVO = BeanUtil.copyProperties(adminModule, AdminModuleVO.class);
+        //查询子集
+        if(adminModule.getType() == 2){
+            List<AdminModule> list = adminModuleService.lambdaQuery().eq(AdminModule::getParentId, adminModule.getModuleId()).list();
+            List<AdminModuleVO> adminModuleVOS = BeanUtil.copyToList(list, AdminModuleVO.class);
+            adminModuleVO.setChildList(adminModuleVOS);
+        }
+        return adminModuleVO;
+    }
+
+
+}

+ 119 - 0
src/main/java/com/gree/mall/contest/logic/admin/AdminRoleLogic.java

@@ -0,0 +1,119 @@
+package com.gree.mall.contest.logic.admin;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.admin.AdminRoleBean;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.enums.admin.RoleTypeEnum;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.AdminRole;
+import com.gree.mall.contest.plus.entity.AdminUser;
+import com.gree.mall.contest.plus.entity.ServiceProvider;
+import com.gree.mall.contest.plus.service.AdminRoleService;
+import com.gree.mall.contest.plus.service.AdminUserService;
+import com.gree.mall.contest.plus.service.ServiceProviderService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class AdminRoleLogic {
+
+    private final AdminRoleService adminRoleService;
+    private final AdminUserService adminUserService;
+    private final CommonLogic commonLogic;
+    private final ServiceProviderService serviceProviderService;
+
+
+    /**
+     * 角色列表
+     */
+    public IPage<AdminRoleBean> listPage(HttpServletRequest request, String adminWebsitId, Integer pageNo, Integer pageSize) {
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+
+        String type = adminRoleService.getById(adminUser.getRoleId()).getType();
+        if("admin".equals(adminUser.getUserName())){
+            type = RoleTypeEnum.A.getCode();
+        }
+
+        Page page = adminRoleService.lambdaQuery()
+                .eq(StringUtils.isNotBlank(adminUser.getServiceProviderId()), AdminRole::getServiceProviderId, adminUser.getServiceProviderId())
+                .eq(StringUtils.isNotBlank(adminWebsitId),AdminRole::getAdminWebsitId,adminWebsitId)
+                .orderByDesc(Arrays.asList(AdminRole::getCreateTime, AdminRole::getType))
+                .page(new Page<>(pageNo, pageSize));
+        List<AdminRoleBean> adminRoleBeans = BeanUtil.copyToList(page.getRecords(), AdminRoleBean.class);
+        for(AdminRoleBean bean : adminRoleBeans){
+            Long count = adminUserService.lambdaQuery().eq(AdminUser::getRoleId, bean.getAdminRoleId()).count();
+            bean.setAccountNum(count);
+        }
+        page.setRecords(adminRoleBeans);
+        return page;
+    }
+
+    /**
+     * 新增角色
+     */
+    public void add(HttpServletRequest request, AdminRole adminRole) {
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+        AdminRole adminRole1= adminRoleService.getById(adminUser.getRoleId());
+        //默认部门角色
+        ServiceProvider serviceProvider = serviceProviderService.getById(adminUser.getServiceProviderId());
+
+        adminRole.setType(StringUtils.isNotBlank(adminUser.getServiceProviderId())?"A":serviceProvider == null ? "C":"B");
+
+        adminRole.setServiceProviderId(adminUser.getServiceProviderId());
+        adminRole.setAdminWebsitId(adminUser.getAdminWebsitId());
+        adminRole.setCreateTime(new Date());
+        adminRole.setCreateBy(adminUser.getNickName());
+        adminRoleService.save(adminRole);
+    }
+
+    /**
+     * 修改角色
+     */
+    @Transactional
+    public void update(AdminRole adminRole) {
+        adminRoleService.updateById(adminRole);
+
+        if (adminRole.getName() != null) {
+            adminUserService.lambdaUpdate()
+                    .set(AdminUser::getRoleName, adminRole.getName())
+                    .eq(AdminUser::getRoleId, adminRole.getAdminRoleId()).update();
+        }
+    }
+
+    /**
+     * 删除角色
+     */
+    public void delete(String adminRoleId) throws RemoteServiceException {
+        //不允许删除使用该角色的帐号
+        Long count = adminUserService.lambdaQuery().eq(AdminUser::getRoleId, adminRoleId).count();
+        if (count > 0) {
+            throw new RemoteServiceException("共有" + count + "个帐号使用该角色,不可删除");
+        }
+        if(adminRoleId.length() < 5){
+            throw new RemoteServiceException("暂不可删除该角色,请联系相关人员");
+        }
+        adminRoleService.removeById(adminRoleId);
+    }
+
+    /**
+     * 详情
+     */
+    public AdminRole detail(String adminRoleId) {
+        return adminRoleService.getById(adminRoleId);
+    }
+
+
+}

+ 600 - 0
src/main/java/com/gree/mall/contest/logic/admin/AdminUserLogic.java

@@ -0,0 +1,600 @@
+package com.gree.mall.contest.logic.admin;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.crypto.SecureUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.gree.mall.contest.bean.SVerification;
+import com.gree.mall.contest.bean.admin.AdminModuleTree;
+import com.gree.mall.contest.bean.admin.AdminUserBean;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.admin.AdminWebsitGrantBean;
+import com.gree.mall.contest.bean.admin.reqDto.AdminUserAddReqBean;
+import com.gree.mall.contest.commonmapper.AdminMapper;
+import com.gree.mall.contest.commonmapper.CommonMapper;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.enums.admin.RoleTypeEnum;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.helper.ResponseHelper;
+import com.gree.mall.contest.logic.SMSLogic;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.*;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.JwtUtils;
+import com.gree.mall.contest.utils.RedisUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletRequest;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AdminUserLogic {
+    private final RedisUtil redisUtil;
+    private final DefaultKaptcha defaultKaptcha;
+    private final CommonLogic commonLogic;
+    private final AdminUserService adminUserService;
+    private final AdminModuleService adminModuleService;
+    private final AdminRoleService adminRoleService;
+    private final AdminUserModuleRelaService adminUserModuleRelaService;
+    private final AdminCompanyWechatService adminCompanyWechatService;
+    private final AdminWebsitService adminWebsitService;
+    private final AdminMapper adminMapper;
+    private final SMSLogic smsLogic;
+    private final ServiceProviderService serviceProviderService;
+    private final MerchantService merchantService;
+    private final CommonMapper commonMapper;
+    private final SysConfigService sysConfigService;
+    private final ServiceBalanceAccService serviceBalanceAccService;
+    private final AdminWebsitLogic adminWebsitLogic;
+
+    @Value("${jwt.expire}")
+    private Long expiration;
+
+    /**
+     * 验证码
+     */
+    public SVerification defaultKaptcha() throws IOException {
+        // 生成文字验证码
+        String text = defaultKaptcha.createText();
+
+        // 生成图片验证码
+        ByteArrayOutputStream outputStream = null;
+        BufferedImage image = defaultKaptcha.createImage(text);
+        outputStream = new ByteArrayOutputStream();
+        ImageIO.write(image, "png", outputStream);
+        byte b[] = outputStream.toByteArray();//从流中获取数据数组。
+//        String baseStr = new BASE64Encoder().encode(b);
+        String baseStr = Base64.getEncoder().encodeToString(b);
+        //生成一个token
+        String code = UUID.randomUUID().toString();
+        redisUtil.set(Constant.RedisPrefix.TOKEN_VERIFICATION + code, text, 2 * 60);
+        SVerification verification = new SVerification();
+        verification.setCode(code);
+        verification.setPic(baseStr);
+        return verification;
+    }
+
+    /**
+     * 登录
+     */
+    public AdminUserBean login(String userName, String password, String code, String codeValue) throws RemoteServiceException {
+
+        AdminUser adminUser = adminUserService.lambdaQuery().eq(AdminUser::getUserName, userName).one();
+        if (adminUser == null) {
+            throw new RemoteServiceException("帐号不存在");
+        }
+        //如果是admin操作非admin账号登陆不需要判断密码是否正确
+        AdminUserCom adminUser1 = commonLogic.getAdminUser();
+        if(adminUser1 == null || !(adminUser1.getUserName().equals("admin")) && !StringUtils.equals(userName,"admin")) {
+
+            String value = (String) redisUtil.get(Constant.RedisPrefix.TOKEN_VERIFICATION + code);
+            if(StringUtils.isBlank(value)){
+                throw new RemoteServiceException("验证码过期,请刷新验证码重试");
+            }
+            if (!value.equals(codeValue)) {
+                throw new RemoteServiceException("验证码错误");
+            }
+
+            if(adminUser.getLoginErrNum() >= 3){
+                throw new RemoteServiceException("登陆失败,您已连续输入错误3次密码,请联系管理人员重置密码");
+            }
+
+            if (!StringUtils.equals(adminUser.getPassword(), SecureUtil.md5(password))) {
+                adminUser.setLoginErrNum(adminUser.getLoginErrNum() + 1);
+                adminUserService.saveOrUpdate(adminUser);
+                //记录错误次数
+                throw new RemoteServiceException("帐号密码错误");
+            }
+        }
+
+        if (!adminUser.getStatus()) {
+            throw new RemoteServiceException("帐号冻结中,请联系相关管理人员");
+        }
+
+        adminUser.setLoginErrNum(0);
+        adminUser.setLastLoginTime(new Date());
+        adminUser.updateById();
+
+        AdminUserBean adminUserBean = new AdminUserBean();
+        BeanUtils.copyProperties(adminUser, adminUserBean);
+
+        // 注入服务对象
+        if (RoleTypeEnum.isServiceProvider(adminUser.getType())) {
+            if (StringUtils.isNotBlank(adminUser.getServiceProviderId())) {
+                //adminUserBean.setServiceProvider(serviceProviderService.getById(adminUser.getServiceProviderId()));
+                //adminUserBean.setIsAllinpay(adminUserBean.getIsAllinpay());
+            }
+        }
+
+        // token到期时间
+        final DateTime offset = DateUtil.offset(DateUtil.date(), DateField.MINUTE, Math.toIntExact(expiration));
+        adminUserBean.setExpireTimeToken(offset);
+
+        //不返回密码
+        adminUserBean.setPassword(null);
+        adminUserBean.setToken(JwtUtils.generateToken(adminUser.getAdminUserId(), adminUser.getUserName(), adminUser.getNickName(), adminUser.getType(), Constant.PC_API));
+        //存放到redis
+        redisUtil.set(Constant.RedisPrefix.TOKEN_MANAGE + adminUserBean.getToken(), adminUser, expiration * 1000);
+
+        return adminUserBean;
+
+    }
+
+    /**
+     * 修改密码
+     */
+    public void updatePassword(String userName, String password, String newPwd) throws RemoteServiceException {
+        String pas = SecureUtil.md5(password);
+        AdminUser adminUser = adminUserService.lambdaQuery()
+                .eq(AdminUser::getUserName, userName)
+                .eq(AdminUser::getPassword, pas).one();
+        if (adminUser == null) {
+            throw new RemoteServiceException("帐号密码错误");
+        }
+        adminUser.setLoginErrNum(0);
+        adminUser.setPassword(SecureUtil.md5(newPwd));
+        adminUserService.updateById(adminUser);
+    }
+
+    /**
+     * 修改帐号状态
+     */
+    public void updateStatus(String adminUserId, Boolean status) {
+        adminUserService.lambdaUpdate().set(AdminUser::getStatus, status).eq(AdminUser::getAdminUserId, adminUserId).update();
+    }
+
+    /**
+     * 重置密码
+     */
+    public void resetPassword(String adminUserId, String password, HttpServletRequest request) throws RemoteServiceException {
+        //检查是否拥有重置的密码权限
+        AdminUserCom adminUser1 = commonLogic.getAdminUser();
+        if (adminUser1.getType().equals(RoleTypeEnum.F.getCode()) || adminUser1.getType().equals(RoleTypeEnum.E.getCode())) {
+            throw new RemoteServiceException("子账号不允许重置密码");
+        }
+        //开始重置
+        AdminUser adminUser = adminUserService.getById(adminUserId);
+
+        if (!StringUtils.isEmpty(adminUser1.getServiceProviderId())
+                && adminUser1.getServiceProviderId().equals(adminUser.getServiceProviderId()) &&
+                (adminUser1.getType().equals(RoleTypeEnum.B.getCode()) || adminUser1.getType().equals(RoleTypeEnum.C.getCode()))
+        ){
+            adminUser.setPassword(SecureUtil.md5(password));
+            adminUser.setLoginErrNum(0);
+            adminUser.updateById();
+
+            return;
+
+        }
+        if (adminUser1.getType().equals(RoleTypeEnum.A.getCode())) {
+            adminUser.setPassword(SecureUtil.md5(password));
+            adminUser.setLoginErrNum(0);
+            adminUser.updateById();
+
+        }else {
+            throw new RemoteServiceException("账号不允许重置别人密码");
+        }
+    }
+
+
+    /**
+     * 新增帐号
+     */
+    @Transactional
+    public void add(AdminUserAddReqBean adminUser) throws RemoteServiceException {
+        AdminUserCom curAdminUser = commonLogic.getAdminUser();
+        if (StringUtils.isBlank(adminUser.getUserName()) || StringUtils.isBlank(adminUser.getPassword())) {
+            throw new RemoteServiceException("参数错误");
+        }
+        Long count = adminUserService.lambdaQuery().eq(AdminUser::getUserName, adminUser.getUserName()).count();
+        if (count > 0) {
+            throw new RemoteServiceException("帐号已被注册");
+        }
+        //当前用户选择的部门
+        AdminWebsit adminWebsit = adminWebsitService.getById(adminUser.getAdminWebsitId());
+        if(adminWebsit == null){
+            throw new RemoteServiceException("请选择部门");
+        }
+        //需要用户roleId
+        AdminRole addUserRole = null;
+        if (StringUtils.isBlank(adminUser.getRoleId()) ||
+                (addUserRole = adminRoleService.getById(adminUser.getRoleId())) == null) {
+            throw new RemoteServiceException("请为用户选择角色");
+        }
+        adminUser.setRoleName(addUserRole.getName());
+
+        if (adminUser.getRoleName().equals("推单员"))
+            throw new RemoteServiceException("推单员需要在推单员列表配置账号");
+
+        if(curAdminUser.getType().equals(RoleTypeEnum.B.getCode())) {
+            adminUser.setType(RoleTypeEnum.D.getCode());
+            adminUser.setServiceProviderId(curAdminUser.getServiceProviderId());
+        }
+        if(curAdminUser.getType().equals(RoleTypeEnum.C.getCode())) {
+            adminUser.setType(RoleTypeEnum.E.getCode());
+            adminUser.setServiceProviderId(curAdminUser.getServiceProviderId());
+        }
+
+        adminUser.setPassword(SecureUtil.md5(adminUser.getPassword()));
+        adminUser.setCreateTime(new Date());
+        adminUserService.save(adminUser);
+
+    }
+
+    /**
+     * 修改帐号
+     */
+    public void update(HttpServletRequest request, AdminUserAddReqBean newUser) {
+
+        AdminUserCom admin = commonLogic.getAdminUser(request);
+        AdminUser oldUser = adminUserService.getById(newUser.getAdminUserId());
+        //需要用户roleId
+        AdminRole addUserRole = null;
+        if (StringUtils.isBlank(newUser.getRoleId()) || (addUserRole = adminRoleService.getById(newUser.getRoleId())) == null) {
+            throw new RemoteServiceException("请为用户选择角色");
+        }
+        //当前用户选择的部门
+        AdminWebsit adminWebsit = adminWebsitService.getById(newUser.getAdminWebsitId());
+        if(adminWebsit == null){
+            throw new RemoteServiceException("请选择部门");
+        }
+        this.setType(admin,oldUser,newUser);
+
+        newUser.setRoleName(addUserRole.getName());
+        newUser.setUserName(null);
+        newUser.setPassword(StringUtils.isBlank(newUser.getPassword()) ? null : SecureUtil.md5(newUser.getPassword()));
+        newUser.updateById();
+
+    }
+
+    /**
+     * 用户类型处理
+     * @param curAdmin
+     * @param oldAdmin
+     * @param newAdmin
+     * @return
+     */
+    public AdminUser setType(AdminUserCom curAdmin,AdminUser oldAdmin,AdminUser newAdmin){
+        if(StringUtils.isBlank(newAdmin.getType())){
+            return newAdmin;
+        }
+        //服务商不可变更服务商信息
+        if(curAdmin.getType().equals(oldAdmin.getType()) && !curAdmin.getUserName().equals("admin")){
+            throw new RemoteServiceException("您暂无权限变更");
+        }
+        //如果是平台添加帐号则为业务员
+        //如果是服务商添加账号则为子账号
+        //如果是合作商添加账号则为合作商子账号
+        if(oldAdmin.getType().equals(RoleTypeEnum.A.getCode())){
+            newAdmin.setType(RoleTypeEnum.A.getCode());
+        }else if(oldAdmin.getType().equals(RoleTypeEnum.B.getCode())){
+            newAdmin.setType(RoleTypeEnum.D.getCode());
+        }else if(oldAdmin.getType().equals(RoleTypeEnum.C.getCode())){
+            newAdmin.setType(RoleTypeEnum.E.getCode());
+        }else{
+            newAdmin.setType(oldAdmin.getType());
+        }
+        return newAdmin;
+    }
+
+    /**
+     * 运营后台用户列表
+     *
+     * @return
+     */
+    public IPage<AdminUser> list(String adminWebsitId,String roleId, Boolean status, String userName, Integer pageNum, Integer pageSize) {
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+
+        List<String> adminWebsitIds = adminUser.getAdminWebsitIds();
+
+        if(StringUtils.isNotBlank(adminWebsitId)) {
+            List<AdminWebsit> list = new ArrayList<>();
+            list.addAll(commonLogic.queryAllChild(list, adminWebsitId));
+            adminWebsitIds = list.stream().map(AdminWebsit::getWebsitId).collect(Collectors.toList());
+            //要把查得父id加入
+            adminWebsitIds.add(adminWebsitId);
+        }
+        IPage<AdminUser> page = adminUserService.lambdaQuery()
+                .in(CollectionUtils.isNotEmpty(adminWebsitIds),AdminUser::getAdminWebsitId,adminWebsitIds)
+                .eq(StringUtils.isNotBlank(roleId), AdminUser::getRoleId, roleId)
+                .eq(status != null, AdminUser::getStatus, status)
+                .and(StringUtils.isNotBlank(userName),item -> item.like(AdminUser::getUserName, userName)
+                .or().like(AdminUser::getNickName,userName)
+                )
+
+                .orderByDesc(AdminUser::getCreateTime)
+                .page(new Page<>(pageNum, pageSize));
+        return page;
+    }
+
+    /**
+     * 运营后台用户详情
+     *
+     * @param adminUserId
+     * @return
+     */
+    public AdminUserBean detail(String adminUserId) {
+        AdminUser adminUser = adminUserService.getById(adminUserId);
+        AdminUserBean adminUserBean = new AdminUserBean();
+        BeanUtils.copyProperties(adminUser, adminUserBean);
+        //服务商对象
+        ServiceProvider serviceProvider = serviceProviderService.getById(adminUser.getServiceProviderId());
+        Merchant merchant = merchantService.getById(adminUser.getServiceProviderId());
+        adminUserBean.setServiceProvider(serviceProvider);
+        adminUserBean.setMerchant(merchant);
+        return adminUserBean;
+    }
+
+
+    /**
+     * 退出登录
+     */
+    public void logout(HttpServletRequest request) {
+        String token = CommonUtils.getToken(request);
+        redisUtil.del(Constant.RedisPrefix.TOKEN_MANAGE + token);
+    }
+
+
+    /**
+     * 根据登录帐号查询权限模块
+     */
+    public List<AdminModuleTree> queryAdminModule(String adminUserId) {
+        //String adminUserId = CommonUtils.getUserId(request);
+        AdminUser adminUser = adminUserService.getById(adminUserId);
+        if (adminUser.getUserName().equals("admin")) {
+            return this.queryAdminModuleTree(null, true, null, null);
+        } else {
+            return this.queryAdminModuleTree(adminUser.getRoleId(), false, null, null);
+        }
+    }
+
+    /**
+     * 给角色授权
+     */
+    @Transactional
+    public void grantModules(AdminWebsitGrantBean adminWebsitGrantBean) {
+        String roleId = adminWebsitGrantBean.getAdminRoleId();
+        //1.清空该角色的权限
+        adminUserModuleRelaService.lambdaUpdate().eq(AdminUserModuleRela::getAdminRoleId, roleId).remove();
+
+        //2.生成权限
+        List<AdminUserModuleRela> list = new ArrayList<>();
+        for (String adminModuleId : adminWebsitGrantBean.getAdminModuleIds()) {
+            AdminUserModuleRela adminUserModuleRela = new AdminUserModuleRela();
+            adminUserModuleRela.setAdminRoleId(roleId);
+            adminUserModuleRela.setCreateTime(new Date());
+            adminUserModuleRela.setStatus(true);
+            adminUserModuleRela.setAdminModuleId(adminModuleId);
+            adminUserModuleRela.setFlag(1);
+            list.add(adminUserModuleRela);
+        }
+        for (String adminModuleId : adminWebsitGrantBean.getAdminModuleIds2()) {
+            AdminUserModuleRela adminUserModuleRela = new AdminUserModuleRela();
+            adminUserModuleRela.setAdminRoleId(roleId);
+            adminUserModuleRela.setCreateTime(new Date());
+            adminUserModuleRela.setStatus(true);
+            adminUserModuleRela.setAdminModuleId(adminModuleId);
+            adminUserModuleRela.setFlag(0);
+            list.add(adminUserModuleRela);
+        }
+        adminUserModuleRelaService.saveBatch(list);
+    }
+
+    /**
+     * 根据角色id查询拥有的权限ids
+     */
+    public List<String> queryModuleIdChecked(String adminRoleId) {
+        AdminRole adminRole = adminRoleService.getById(adminRoleId);
+        Boolean admin = adminRole.getName().equals("超级管理员");
+        if (admin) {
+            List<AdminModule> list = adminModuleService.list();
+            if (list.size() == 0) {
+                return new ArrayList<>();
+            }
+            return list.stream().map(AdminModule::getModuleId).collect(Collectors.toList());
+        }
+        List<AdminUserModuleRela> list = adminUserModuleRelaService.lambdaQuery()
+                .eq(AdminUserModuleRela::getFlag,1)
+                .eq(AdminUserModuleRela::getAdminRoleId, adminRoleId).list();
+        if (CollectionUtils.isEmpty(list)) {
+            return new ArrayList<>();
+        }
+        return list.stream().map(AdminUserModuleRela::getAdminModuleId).collect(Collectors.toList());
+    }
+
+    /**
+     * 根据角色查询所有功能模块权限
+     */
+    public List<AdminModuleTree> queryAllAdminModuleTree(String adminRoleId, HttpServletRequest request) {
+        String userId = CommonUtils.getUserId(request);
+        AdminUser adminUser = adminUserService.getById(userId);
+
+        //更改的角色
+        AdminRole adminRole = adminRoleService.getById(adminRoleId);
+        //查询该角色有的权限d
+        List<AdminUserModuleRela> hasModules = adminUserModuleRelaService.lambdaQuery().eq(AdminUserModuleRela::getAdminRoleId, adminRoleId).list();
+        List<String> collect = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(hasModules)) {
+            collect = hasModules.stream().map(AdminUserModuleRela::getAdminModuleId).collect(Collectors.toList());
+        }
+        List<AdminModuleTree> adminModuleTrees = this.queryAdminModuleTree(adminRoleId, adminRole.getName().equals("超级管理员"), collect, adminUser);
+        return adminModuleTrees;
+    }
+
+
+    private List<AdminModuleTree> queryAdminModuleTree(String roleId, Boolean admin, List<String> hasModule, AdminUser adminUser) {
+
+        List<AdminModule> adminModules = null;
+
+        if (admin != null && !admin) {
+            //非超管角色
+            List<AdminUserModuleRela> list = adminUserModuleRelaService.lambdaQuery()
+                    .eq(StringUtils.isNotBlank(roleId), AdminUserModuleRela::getAdminRoleId, roleId)
+                    .list();
+            if (list.size() == 0) {
+                return new ArrayList<>();
+            }
+            List<String> moduleIds = list.stream().map(AdminUserModuleRela::getAdminModuleId).collect(Collectors.toList());
+            adminModules = adminModuleService.lambdaQuery().in(AdminModule::getModuleId, moduleIds)
+                    .orderByAsc(AdminModule::getSortNum).list();
+        } else {
+            //超管角色
+            adminModules = adminModuleService.lambdaQuery().orderByAsc(AdminModule::getSortNum).list();
+        }
+        List<AdminModuleTree> trees = new ArrayList<>();
+        for (AdminModule adminModule : adminModules) {
+            AdminModuleTree adminModuleTree = new AdminModuleTree();
+            BeanUtils.copyProperties(adminModule, adminModuleTree);
+            if ((hasModule != null && hasModule.contains(adminModule.getModuleId())) || admin) {
+                adminModuleTree.setShow(true);
+            }
+            trees.add(adminModuleTree);
+        }
+        List<AdminModuleTree> parents = trees.stream().filter(v -> v.getLevel() == 1).collect(Collectors.toList());
+        for (AdminModuleTree adminModuleTree : parents) {
+            adminModuleTree.setChildren(this.treeModule(trees, adminModuleTree.getModuleId(), hasModule, admin));
+        }
+        return parents;
+    }
+
+    private List<AdminModuleTree> treeModule(List<AdminModuleTree> list, String parentId, List<String> hasModule, Boolean admin) {
+        //最父级资源树
+        if (StringUtils.isBlank(parentId)) {
+            return new ArrayList<>();
+        }
+        List<AdminModuleTree> collect = list.stream().filter(e -> e.getParentId().equals(parentId)).collect(Collectors.toList());
+        for (AdminModuleTree bean : collect) {
+            if ((hasModule != null && hasModule.contains(bean.getModuleId())) || admin) {
+                bean.setShow(true);
+            }
+            bean.setChildren(this.treeModule(list, bean.getModuleId(), hasModule, admin));
+        }
+        return collect;
+    }
+
+    /**
+     * 找回密码-获取验证码
+     * @param userName
+     * @param mobile
+     * @param code
+     * @return
+     */
+    public AdminUserBean resetPasswordGetCode(String userName,String mobile, String code) {
+        smsLogic.checkSmsCode(mobile, code, "RESET");
+
+        final AdminUser adminUser = adminUserService.lambdaQuery()
+                .eq(AdminUser::getUserName,userName)
+                .eq(AdminUser::getLinkPhone, mobile)
+                .one();
+
+        if (Objects.isNull(adminUser)) {
+            throw new RemoteServiceException("账号不存在");
+        }
+
+        AdminUserBean bean = new AdminUserBean();
+        bean.setAdminUserId(adminUser.getAdminUserId());
+        bean.setUserName(adminUser.getUserName());
+        bean.setToken(IdUtil.simpleUUID());
+
+        redisUtil.set(adminUser.getAdminUserId() + ":" + bean.getToken(), bean.getToken(), 5*60);
+        return bean;
+    }
+
+    /**
+     * 找回密码-更新密码
+     * @param adminUserId
+     * @param token
+     * @param newPassword
+     * @param confirmPassword
+     */
+    public void resetPasswordUpdate(String adminUserId, String token, String newPassword, String confirmPassword) {
+        String redisKey = adminUserId + ":" + token;
+        if (!redisUtil.hasKey(redisKey)) {
+            throw new RemoteServiceException("超时操作,请重新找回");
+        }
+
+        String value = (String)redisUtil.get(redisKey);
+        if(!StringUtils.equals(value, token)) {
+            throw new RemoteServiceException("修改密码,参数异常");
+        }
+
+        if (!StringUtils.equals(newPassword, confirmPassword)) {
+            throw new RemoteServiceException("新密码与确认密码不匹配");
+        }
+
+        adminUserService.lambdaUpdate()
+                .set(AdminUser::getPassword, SecureUtil.md5(newPassword))
+                .eq(AdminUser::getAdminUserId, adminUserId)
+                .update();
+
+        redisUtil.del(redisKey);
+    }
+
+    public void serviceUpdateMpMobile(String mobile) {
+        final AdminUserCom adminUser = commonLogic.getAdminUser();
+        if (!(adminUser.getType().equals(RoleTypeEnum.B.getCode())
+                || adminUser.getType().equals(RoleTypeEnum.D.getCode()))) {
+            throw new RemoteServiceException("非服务商不能修改");
+        }
+
+        serviceProviderService.lambdaUpdate()
+                .set(ServiceProvider::getReceiveMpMobile, mobile)
+                .eq(ServiceProvider::getId, adminUser.getServiceProviderId())
+                .update();
+    }
+
+    public AdminUserBean renewal(String token) {
+        final Object o = redisUtil.get(Constant.RedisPrefix.TOKEN_MANAGE + token);
+        if (Objects.isNull(o)) {
+            throw new RemoteServiceException(ResponseHelper.ResponseCode_AUTH_ERROR, "登录过期");
+        }
+
+        AdminUserBean bean = (AdminUserBean) o;
+        final AdminUser adminUser = adminUserService.getById(bean.getAdminUserId());
+
+        bean.setToken(JwtUtils.refreshToken(token, Constant.PC_API));
+
+        //存放到redis
+        redisUtil.set(Constant.RedisPrefix.TOKEN_MANAGE + bean.getToken(), adminUser, 2 * 60 * 60 * 1000);
+        return bean;
+    }
+}

+ 302 - 0
src/main/java/com/gree/mall/contest/logic/admin/AdminWebsitLogic.java

@@ -0,0 +1,302 @@
+package com.gree.mall.contest.logic.admin;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.admin.AdminWebsitTree;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AdminWebsitLogic {
+
+    private final AdminWebsitService adminWebsitService;
+    private final AdminWebsitRegionService adminWebsitRegionService;
+    private final ServiceProviderService serviceProviderService;
+    private final AdminUserService adminUserService;
+    private final CommonLogic commonLogic;
+
+    private final AdminRoleService adminRoleService;
+
+    /**
+     * 部门树
+     */
+    public List<AdminWebsitTree> tree(HttpServletRequest request, Boolean isRole){
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+
+        List<AdminRole> adminRoles = adminRoleService.lambdaQuery().in(CollectionUtil.isNotEmpty(adminUser.getAdminWebsitIds())
+                ,AdminRole::getServiceProviderId,adminUser.getAdminWebsitIds()
+        )
+                .list();
+        List<String> websids = adminRoles.stream().map(AdminRole::getAdminWebsitId).collect(Collectors.toList());
+
+
+        if (isRole){
+            if (CollectionUtil.isNotEmpty(adminUser.getAdminWebsitIds()))
+                websids.addAll(adminUser.getAdminWebsitIds());
+            List<AdminWebsit> list = adminWebsitService.lambdaQuery().in(AdminWebsit::getWebsitId,websids)
+                    .orderByAsc(AdminWebsit::getCreateTime)
+                    .list();
+
+            List<AdminWebsitTree> adminWebsitTrees = BeanUtil.copyToList(list, AdminWebsitTree.class);
+
+            return adminWebsitTrees;
+        }
+
+        List<AdminWebsit> list = adminWebsitService.list();
+        List<AdminWebsitTree> trees = new ArrayList<>();
+
+
+        for(AdminWebsit adminWebsit : list){
+            AdminWebsitTree adminWebsitTree = new AdminWebsitTree();
+            BeanUtils.copyProperties(adminWebsit,adminWebsitTree);
+            trees.add(adminWebsitTree);
+        }
+        List<AdminWebsitTree> parentList = trees.stream()
+                .filter(x ->  x.getWebsitId().equals(adminUser.getAdminWebsitId())).collect(Collectors.toList());
+
+        for(AdminWebsitTree adminWebsit:parentList){
+            adminWebsit.setChildren(this.treeModule(trees,adminWebsit.getWebsitId()));
+        }
+        return parentList;
+    }
+
+
+    public List<AdminWebsit> list(Boolean isAll,Boolean status){
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+
+        List<AdminWebsit> list = adminWebsitService.lambdaQuery()
+                .eq(status != null,AdminWebsit::getStatus, status)
+                .in(CollectionUtil.isNotEmpty(adminUser.getAdminWebsitIds()) && (isAll == null || !isAll),AdminWebsit::getWebsitId,adminUser.getAdminWebsitIds())
+                .list();
+        return list;
+    }
+
+
+    /**
+     *  新增部门
+     */
+    @Transactional
+    public void add(AdminWebsit adminWebsit, HttpServletRequest request) throws WxErrorException, RemoteServiceException {
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+
+        Long count = adminWebsitService.lambdaQuery().eq(AdminWebsit::getName, adminWebsit.getName())
+                .count();
+        if(count > 0){
+            throw new RemoteServiceException("部门名称已存在");
+        }
+        AdminWebsit parent = adminWebsitService.getById(adminWebsit.getParentId());
+        if(parent == null){
+            throw new RemoteServiceException("请选择父部门");
+        }
+        List<AdminWebsit> list = new ArrayList<>();
+        list.addAll(commonLogic.queryAllChild(list,adminWebsit.getWebsitId()));
+        List<String> childWebsitIds = list.stream().map(AdminWebsit::getWebsitId).collect(Collectors.toList());
+        if(childWebsitIds.contains(adminWebsit.getParentId())){
+            throw new RemoteServiceException("父级部门不可以是自己或者自己的下级部门");
+        }
+        adminWebsit.setType(parent.getType());
+        adminWebsit.setWebsitId(IdWorker.getIdStr());
+        adminWebsitService.save(adminWebsit);
+    }
+    /**
+     * 编辑部门
+     */
+    @Transactional
+    public void update(AdminWebsit adminWebsit,HttpServletRequest request) throws RemoteServiceException, WxErrorException {
+        AdminWebsit old = adminWebsitService.getById(adminWebsit.getWebsitId());
+        if(Constant.SYS_WEBSIT_NAME.contains(old.getName())){
+            throw new RemoteServiceException("不可操作当前部门信息");
+        }
+        Long count = adminWebsitService.lambdaQuery()
+                .ne(AdminWebsit::getWebsitId,adminWebsit.getWebsitId())
+                .eq(AdminWebsit::getName, adminWebsit.getName())
+                .count();
+        if(count > 0){
+            throw new RemoteServiceException("部门名称或部门编号已存在");
+        }
+        if(adminWebsit.getParentId().equals("0")){
+            throw new RemoteServiceException("请选择父部门");
+        }
+        AdminWebsit parent = adminWebsitService.getById(adminWebsit.getParentId());
+        if(parent == null){
+            throw new RemoteServiceException("请选择父部门");
+        }
+        if(StringUtils.equals(adminWebsit.getWebsitId(),adminWebsit.getParentId())){
+            throw new RemoteServiceException("父级部门不可以是自己");
+        }
+        List<AdminWebsit> list = new ArrayList<>();
+        list.addAll(commonLogic.queryAllChild(list,adminWebsit.getWebsitId()));
+        List<String> childWebsitIds = list.stream().map(AdminWebsit::getWebsitId).collect(Collectors.toList());
+        if(childWebsitIds.contains(adminWebsit.getParentId())){
+            throw new RemoteServiceException("父级部门不可以是自己或者自己的下级部门");
+        }
+        adminWebsit.setType(parent.getType());
+        adminWebsitService.saveOrUpdate(adminWebsit);
+        //workWechatLogic.updateOrganization(adminWebsit.getWebsitId(),adminWebsit.getName(),adminWebsit.getParentId(),adminUser.getAdminCompanyWechat());
+    }
+
+    /**
+     * 删除部门
+     */
+    @Transactional
+    public void delete(String websitId,HttpServletRequest request) throws WxErrorException {
+        AdminWebsit old = adminWebsitService.getById(websitId);
+        if(Constant.SYS_WEBSIT_NAME.contains(old.getName())){
+            throw new RemoteServiceException("不可操作当前部门信息");
+        }
+        Long count = adminWebsitService.lambdaQuery().eq(AdminWebsit::getParentId, websitId).count();
+        if(count > 0){
+            throw new RemoteServiceException("当前部门存在子级,删除失败");
+        }
+        Long count1 = adminUserService.lambdaQuery().eq(AdminUser::getAdminWebsitId, websitId).count();
+        if(count1 > 0){
+            throw new RemoteServiceException("当前部门存在账号,删除失败");
+        }
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+        //递归删除
+        List<AdminWebsit> list = new ArrayList<>();
+        list.addAll(commonLogic.queryAllChild(list,websitId));
+        List<String> ids = list.stream().map(AdminWebsit::getWebsitId).collect(Collectors.toList());
+        ids.add(websitId);
+        if(ids.size() > 0) {
+//            for(String idd : ids){
+//                workWechatLogic.deleteOrganization(idd,adminUser.getAdminCompanyWechat());
+//            }
+            adminWebsitService.lambdaUpdate().in(AdminWebsit::getWebsitId, ids).remove();
+        }
+    }
+
+    /**
+     * 详情
+     * @param id
+     */
+    public AdminWebsit detail(String id){
+        AdminWebsit adminWebsit = adminWebsitService.getById(id);
+        return adminWebsit;
+    }
+
+    /**
+     * 递归组装数据
+     * @param list
+     * @param parentId
+     * @return
+     */
+    private List<AdminWebsitTree> treeModule(List<AdminWebsitTree> list, String parentId){
+        //最父级资源树
+        if(parentId == null || parentId.equals("0")) {
+            return null;
+        }
+        List<AdminWebsitTree> collect = list.stream()
+                .filter(e -> e.getParentId().equals(parentId)).collect(Collectors.toList());
+        if(collect.size() == 0){
+            return null;
+        }
+        for(AdminWebsitTree bean : collect){
+            bean.setChildren(this.treeModule(list, bean.getWebsitId()));
+        }
+        return collect;
+    }
+
+
+
+    /**
+     * 保存部门的服务区域
+     * @param adminWebsitRegions
+     */
+    @Transactional
+    public void saveRegion(List<AdminWebsitRegion> adminWebsitRegions){
+        List<String> adminWebsitIds = adminWebsitRegions.stream().map(AdminWebsitRegion::getAdminWebsitId).distinct().collect(Collectors.toList());
+        if(adminWebsitIds.size() != 1){
+            throw new RemoteServiceException("参数异常,请检查所有部门id是否一致");
+        }
+//        if(adminWebsitRegions.size() > 1){
+//            throw new RemoteServiceException("暂只支持1个个保存");
+//        }
+        for(AdminWebsitRegion adminWebsitRegion : adminWebsitRegions) {
+            Long count = adminWebsitRegionService.lambdaQuery()
+                    .eq(AdminWebsitRegion::getAdminWebsitId, adminWebsitRegion.getAdminWebsitId())
+                    .eq(AdminWebsitRegion::getServiceCategoryId, adminWebsitRegion.getServiceCategoryId())
+                    .eq(AdminWebsitRegion::getProvinceId, adminWebsitRegion.getProvinceId())
+                    .eq(AdminWebsitRegion::getCityId, adminWebsitRegion.getCityId())
+                    .eq(AdminWebsitRegion::getAreaId, adminWebsitRegion.getAreaId())
+                    .eq(AdminWebsitRegion::getStreetId, adminWebsitRegion.getStreetId())
+                    .count();
+            String pcas = adminWebsitRegion.getProvince() + adminWebsitRegion.getCity() + adminWebsitRegion.getArea() + adminWebsitRegion.getStreet();
+            if (count > 0) {
+                throw new RemoteServiceException(pcas + "已存在,请勿重复添加");
+            }
+            adminWebsitRegion.setPcas(pcas);
+            adminWebsitRegionService.save(adminWebsitRegion);
+        }
+        //刷新服务商的服务类目冗余字段
+        reloadServiceCategoryName(adminWebsitRegions.get(0).getAdminWebsitId());
+    }
+
+    /**
+     * 部门的服务区域列表
+     * @param adminWebsitId
+     * @return
+     */
+    public List<AdminWebsitRegion> listRegion(String adminWebsitId,String keyword,String serviceCategoryId){
+        List<AdminWebsitRegion> list = adminWebsitRegionService.lambdaQuery()
+                .like(StringUtils.isNotBlank(keyword),AdminWebsitRegion::getPcas,keyword)
+                .eq(StringUtils.isNotBlank(serviceCategoryId),AdminWebsitRegion::getServiceCategoryId,serviceCategoryId)
+                .eq(AdminWebsitRegion::getAdminWebsitId, adminWebsitId).list();
+        return list;
+    }
+
+    /**
+     * 删除服务区域
+     */
+    public void deleteRegion(String id){
+        AdminWebsitRegion adminWebsitRegion = adminWebsitRegionService.getById(id);
+        adminWebsitRegionService.removeById(id);
+        //刷新服务商的服务类目冗余字段
+        reloadServiceCategoryName(adminWebsitRegion.getAdminWebsitId());
+    }
+
+
+    /**
+     * 刷新服务商的服务类目id
+     * @param adminWebsitId
+     * @return
+     */
+    public void reloadServiceCategoryName(String adminWebsitId){
+        String serviceCategoryName = adminWebsitRegionService.lambdaQuery().eq(AdminWebsitRegion::getAdminWebsitId, adminWebsitId)
+                .list().stream()
+                .map(AdminWebsitRegion::getServiceCategoryName)
+                .distinct()
+                .collect(Collectors.joining());
+
+        serviceProviderService.lambdaUpdate()
+                .set(ServiceProvider::getServiceCategoryName,serviceCategoryName)
+                .eq(ServiceProvider::getId,adminWebsitId).update();
+    }
+
+
+
+
+
+
+
+
+}

+ 75 - 0
src/main/java/com/gree/mall/contest/logic/admin/OperationLogLogic.java

@@ -0,0 +1,75 @@
+package com.gree.mall.contest.logic.admin;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.logic.common.CommonLogic;
+import com.gree.mall.contest.plus.entity.OperationLog;
+import com.gree.mall.contest.plus.service.OperationLogService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class OperationLogLogic {
+
+    private final OperationLogService operationLogService;
+
+    private final CommonLogic commonLogic;
+
+    /**
+     * 操作日志列表分页
+     */
+    public IPage<OperationLog> listPage(HttpServletRequest request, String moduleName, String companyWechatName, String nickName, String startTime, String endTime, Integer pageNo, Integer pageSize){
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+        List<String> companyWechatIds = adminUser.getCompanyWechatIds();
+
+        return operationLogService.lambdaQuery()
+                .in(CollectionUtils.isNotEmpty(companyWechatIds),OperationLog::getCompanyWechatId,companyWechatIds)
+                .likeRight(StringUtils.isNotBlank(companyWechatName),OperationLog::getCompanyName,companyWechatName)
+                .likeRight(StringUtils.isNotBlank(nickName),OperationLog::getNickName,nickName)
+                .like(OperationLog::getModuleName,moduleName)
+                .between(StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime),
+                        OperationLog::getCreateTime,startTime,endTime)
+                .orderByDesc(Arrays.asList(OperationLog::getCreateTime,OperationLog::getOperationLogId))
+                .page(new Page<>(pageNo,pageSize));
+    }
+
+    /**
+     * 所属模块
+     */
+    public List<String> moduleList(HttpServletRequest request){
+        AdminUserCom adminUser = commonLogic.getAdminUser(request);
+        List<String> companyWechatIds = adminUser.getCompanyWechatIds();
+
+        List<OperationLog> distinctModuleName = operationLogService.lambdaQuery()
+                .select(OperationLog::getModuleName)
+                .in(CollectionUtils.isNotEmpty(companyWechatIds),OperationLog::getCompanyWechatId,companyWechatIds)
+                .groupBy(OperationLog::getModuleName)
+                .list();
+        if(distinctModuleName.size() == 0){
+            return new ArrayList<>();
+        }
+        List<String> collect = distinctModuleName.stream().map(OperationLog::getModuleName).distinct().collect(Collectors.toList());
+        return collect;
+    }
+
+    /**
+     * 详情
+     */
+    public OperationLog detail(String operationLogId){
+        return operationLogService.getById(operationLogId);
+    }
+
+
+}

+ 39 - 0
src/main/java/com/gree/mall/contest/logic/admin/SysConfigLogic.java

@@ -0,0 +1,39 @@
+package com.gree.mall.contest.logic.admin;
+
+import com.gree.mall.contest.plus.entity.SysConfig;
+import com.gree.mall.contest.plus.service.SysConfigService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * 系统应用配置
+ * @author :lijh
+ * @description:TODO
+ * @date :2024/3/16 13:45
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SysConfigLogic {
+
+    private final SysConfigService sysConfigService;
+
+    /**
+     * 保存
+     * @param sysConfig
+     */
+    public void save(SysConfig sysConfig){
+        sysConfig.setId("1");
+        sysConfig.insertOrUpdate();
+    }
+
+    /**
+     * 详情
+     * @return
+     */
+    public SysConfig detail(){
+        return sysConfigService.getById("1");
+    }
+
+}

+ 316 - 0
src/main/java/com/gree/mall/contest/logic/common/CommonLogic.java

@@ -0,0 +1,316 @@
+package com.gree.mall.contest.logic.common;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.ListUtil;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.commonmapper.AdminMapper;
+import com.gree.mall.contest.enums.StateEnum;
+import com.gree.mall.contest.enums.admin.RoleTypeEnum;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.plus.entity.*;
+import com.gree.mall.contest.plus.service.*;
+import com.gree.mall.contest.utils.ApplicationContextUtils;
+import com.gree.mall.contest.utils.CommonUtils;
+import com.gree.mall.contest.utils.RedisUtil;
+import com.gree.mall.contest.utils.oss.OSSUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class CommonLogic {
+
+    @Value("${spring.profiles.active}")
+    private String active;
+    @Autowired
+    WechatLogic wechatLogic;
+    @Autowired
+    RedisUtil redisUtil;
+    @Autowired
+    AdminCompanyWechatService AdminCompanyWechatService;
+    @Autowired
+    CommonFileService commonFileService;
+    @Autowired
+    RegionService regionService;
+    @Autowired
+    AdminUserService adminUserService;
+    @Autowired
+    AdminCompanyWechatService adminCompanyWechatService;
+    @Autowired
+    AdminMapper adminMapper;
+    @Autowired
+    AdminWebsitService adminWebsitService;
+    @Autowired
+    AdminWebsitRegionService adminWebsitRegionService;
+    @Autowired
+    UserService userService;
+    @Autowired
+    OSSUtil ossUtil;
+    @Autowired
+    SysConfigService sysConfigService;
+
+    @Autowired
+    LbsAmapService lbsAmapService;
+
+    public Map<String, String> getOSSConfig() throws UnsupportedEncodingException {
+        return ossUtil.getConfig();
+    }
+
+    public SysConfig getSysConfig(){
+        SysConfig sysConfig = sysConfigService.lambdaQuery().last("limit 1").one();
+        if(sysConfig == null){
+            throw new RemoteServiceException("系统缺少必要配置,请联系相关人员");
+        }
+        return sysConfig;
+    }
+
+    /**
+     * 查询服务商的所有账号
+     * @param serviceProviderId
+     * @return
+     */
+    public List<AdminUser> getAdminUserByServiceProviderId(String serviceProviderId){
+        List<AdminUser> list = adminUserService.lambdaQuery()
+                .eq(AdminUser::getStatus,true)
+                .eq(AdminUser::getServiceProviderId, serviceProviderId).list();
+        return list;
+    }
+
+
+    /**
+     * 获取用户信息
+     * @param mobile
+     * @param flag
+     * @return
+     */
+    public User getUserByMobile(String mobile,Integer flag){
+        if(StringUtils.isBlank(mobile) || flag == null){
+            throw new RemoteServiceException("获取用户信息失败,缺少必要的参数");
+        }
+        return userService.lambdaQuery()
+                .eq(User::getMobile,mobile)
+                .eq(User::getFlag,flag)
+                .one();
+    }
+
+    /**
+     * 查询当前PC用户
+     * @return
+     */
+    public AdminUserCom getAdminUser(){
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return this.getAdminUser(request);
+    }
+
+    /**
+     * 获取帐号以及部门id集合
+     */
+    public AdminUserCom getAdminUser(HttpServletRequest request){
+        String userId = CommonUtils.getUserId(request);
+        //方便测试用
+        if(StringUtils.isBlank(userId) && !active.equals("prd")){
+            AdminUser admin = adminUserService.lambdaQuery().eq(AdminUser::getUserName, "admin").one();
+            AdminUserCom adminUserCom = new AdminUserCom();
+            BeanUtils.copyProperties(admin,adminUserCom);
+            AdminCompanyWechat adminCompanyWechat = adminCompanyWechatService.getById("1");
+            adminUserCom.setAdminCompanyWechat(adminCompanyWechat);
+            return adminUserCom;
+        }
+        AdminUser adminUser = adminUserService.getById(userId);
+        if(adminUser == null){
+            return null;
+        }
+        if(!adminUser.getStatus()){
+            throw new RemoteServiceException("帐号已被禁用");
+        }
+        AdminUserCom adminUserCom = new AdminUserCom();
+        BeanUtils.copyProperties(adminUser,adminUserCom);
+
+        //所有帐号
+        AdminUserCom account2 = this.websitAccount(adminUserCom,request);
+
+        if(account2 != null){
+            return account2;
+        }
+        adminUserCom.setCompanyWechatIds(ListUtil.toList("1"));
+        //暂不过滤权限
+        //adminUserCom.setAdminWebsitIds(null);
+        return adminUserCom;
+    }
+
+    /**
+     * 部门帐号
+     */
+    public AdminUserCom websitAccount(AdminUserCom adminUserCom,HttpServletRequest request){
+        AdminWebsit adminWebsit = adminWebsitService.getById(adminUserCom.getAdminWebsitId());
+        adminUserCom.setAdminWebsit(adminWebsit);
+        //非平台账号
+        if(!adminUserCom.getUserName().equals("admin")){
+            List<AdminWebsit> list = new ArrayList<>();
+            list.addAll(this.queryAllChild(list, adminUserCom.getAdminWebsitId()));
+            List<String> websitIds = list.stream().map(AdminWebsit::getWebsitId).distinct().collect(Collectors.toList());
+            websitIds.add(adminUserCom.getAdminWebsitId());
+            adminUserCom.setAdminWebsitIds(websitIds.stream().distinct().collect(Collectors.toList()));
+
+            //平台业务员的区id
+            if(StringUtils.equals(adminUserCom.getType(), RoleTypeEnum.A.getCode())) {
+                //区域id
+                List<AdminWebsitRegion> adminWebsitRegions = adminWebsitRegionService.lambdaQuery().in(AdminWebsitRegion::getAdminWebsitId, adminUserCom.getAdminWebsitIds()).list();
+                adminUserCom.setAreaIds(adminWebsitRegions.stream().map(AdminWebsitRegion::getAreaId).distinct().collect(Collectors.toList()));
+            }
+
+        }
+
+        return adminUserCom;
+    }
+
+    public User getUser(){
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        String userId = CommonUtils.getUserId(request);
+        User user = userService.getById(userId);
+        return user;
+    }
+
+    /**
+     * 递归查询所有数据
+     */
+    public List<AdminWebsit> queryAllChild(List<AdminWebsit> list,String id){
+        if(id == null || id.equals("0")){
+            return list;
+        }
+        List<AdminWebsit> adminWebsits = adminWebsitService.lambdaQuery().eq(AdminWebsit::getParentId, id).list();
+        if(adminWebsits.size() == 0){
+            return list;
+        }
+        for(AdminWebsit adminWebsit:adminWebsits){
+            this.queryAllChild(list,adminWebsit.getWebsitId());
+        }
+        list.addAll(adminWebsits);
+        return list;
+    }
+
+    /**
+     * 上传附件
+     * @param file
+     * @return
+     * @throws IOException
+     */
+    public CommonFile uploadFile( MultipartFile file) throws IOException, RemoteServiceException {
+
+        if (file == null || file.isEmpty()) {
+            return null;
+        }
+        String uploadFilePath = file.getOriginalFilename().replaceAll("\\\\", "/");
+        String uploadFileName = uploadFilePath.substring(
+                uploadFilePath.lastIndexOf('/') + 1, uploadFilePath.lastIndexOf('.'));
+        String uploadFileSuffix = uploadFilePath.substring(uploadFilePath.lastIndexOf('.') + 1, uploadFilePath.length());
+        long currentTime = System.currentTimeMillis();
+        BufferedOutputStream bufferedOutputStream = null;
+
+        //限制大小为10m
+        if (file.getSize() > 1024 * 1024 * 30) {
+            throw new RemoteServiceException("文件大小超出限制");
+        }
+
+        try {
+            String filePath = ossUtil.getFilePath() + "/" + currentTime + UUID.randomUUID().toString() + "." + uploadFileSuffix;
+            if (!ossUtil.uploadFile(filePath, file.getBytes())) {
+                throw new RemoteServiceException("文件同步oss失败");
+            }
+            String ossUrl = ossUtil.getAccessUrl() + filePath;
+
+            CommonFile commonFile = new CommonFile();
+            commonFile.setUrl(ossUrl);
+            commonFile.setName(uploadFileName);
+            commonFile.setFileType(uploadFileSuffix);
+            commonFile.setFileSize(BigDecimal.valueOf(file.getSize()));
+            commonFile.setCreateTime(new Date());
+            commonFileService.save(commonFile);
+            return commonFile;
+        } finally {
+            if (bufferedOutputStream != null) {
+                bufferedOutputStream.close();
+            }
+        }
+    }
+
+
+    public List<Region> queryRegionList(List<String> pid){
+        return regionService.lambdaQuery().in(Region::getPid,pid).list();
+    }
+
+    public String getFile(String key) throws IOException {
+        String url = ossUtil.getUrlWw(key);
+        return url;
+    }
+
+    public List<LbsAmap> city2(String city){
+        List<LbsAmap> citys = lbsAmapService.lambdaQuery()
+                .eq(LbsAmap::getStatus, StateEnum.ON.getKey())
+                .like(LbsAmap::getName, city)
+                .eq(LbsAmap::getLevel, "city")
+                .list();
+        return citys;
+    }
+
+
+    public AdminCompanyWechat getAdminCompanyWechat(String subAppId){
+        AdminCompanyWechat one = adminCompanyWechatService.lambdaQuery().eq(AdminCompanyWechat::getSubAppId, subAppId).last("limit 1").one();
+        return one;
+    }
+
+    /**
+     * 附件归属绑定
+     */
+    public void bindFileFile(String objId,String objType,List<CommonFile> fileIds){
+        if(CollectionUtil.isEmpty(fileIds)){
+            return;
+        }
+        //先删除后绑定
+        commonFileService.lambdaUpdate()
+                .eq(CommonFile::getObjId,objId)
+                .eq(CommonFile::getObjType,objType).remove();
+
+        for(CommonFile file : fileIds) {
+            file.setId(null);
+            file.setObjId(objId);
+            file.setObjType(objType);
+        }
+        commonFileService.saveBatch(fileIds);
+    }
+
+    /**
+     * 查询附件列表
+     */
+    public List<CommonFile> queryFileByObjId(String objId,String objType){
+        return commonFileService.lambdaQuery()
+                .eq(CommonFile::getObjId,objId)
+                .eq(CommonFile::getObjType,objType)
+                .orderByAsc(CommonFile::getCreateTime)
+                .list();
+    }
+
+    /**
+     * 查询附件列表
+     */
+    public List<String> queryFileUrlsByObjId(String objId,String objType){
+        List<CommonFile> commonFiles = this.queryFileByObjId(objId, objType);
+        if(commonFiles.size() == 0){
+            return null;
+        }
+        return commonFiles.stream().map(CommonFile::getUrl).collect(Collectors.toList());
+    }
+}

+ 243 - 0
src/main/java/com/gree/mall/contest/logic/common/WechatLogic.java

@@ -0,0 +1,243 @@
+package com.gree.mall.contest.logic.common;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.hutool.json.JSONUtil;
+import com.gree.mall.contest.bean.common.WechatOpenBean;
+import com.gree.mall.contest.config.wx.WxConfiguration;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import com.gree.mall.contest.plus.entity.WxScene;
+import com.gree.mall.contest.plus.service.WxSceneService;
+import com.gree.mall.contest.utils.oss.OSSUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpQrcodeService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+/**
+ * Created by lijh on 19/06/28.
+ */
+
+@RequiredArgsConstructor
+@Component
+@Slf4j
+public class WechatLogic {
+
+    //环境
+    @Value("${spring.profiles.active}")
+    public String env;
+
+    private final OSSUtil ossUtil;
+    private final WxSceneService wxSceneService;
+
+    /**
+     * 微信小程序服务
+     * @return
+     */
+    public WxMaService getMaService(){
+        return this.getMaService("1");
+    }
+    public WxMaService getMaService(String companyWechatId){
+        return WxConfiguration.wxMaService.get(companyWechatId);
+    }
+    /**
+     * 微信公众号服务
+     * @return
+     */
+    public WxMpService getMpService(){
+        return getMpService("1");
+    }
+    public WxMpService getMpService(String companyWechatId){
+        return WxConfiguration.wxMpServices.get(companyWechatId);
+    }
+    /**
+     * 微信公众号服务接收推送消息
+     * @return
+     */
+    public WxMpMessageRouter getMpMessage(){
+        return getMpMessage("1");
+    }
+    public WxMpMessageRouter getMpMessage(String companyWechatId){
+        return WxConfiguration.wxMpMessageRouters.get(companyWechatId);
+    }
+    /**
+     * 获取微信token
+     *
+     * @return
+     * @throws RemoteServiceException
+     */
+    public String getAccessToken() throws RemoteServiceException, WxErrorException {
+        return this.getMaService().getAccessToken();
+    }
+
+    /**
+     * 小程序授权openid
+     * @param code
+     * @return
+     * @throws RemoteServiceException
+     */
+    public WechatOpenBean authToken(String code, String appid) throws RemoteServiceException {
+        try {
+            WxMaJscode2SessionResult wxMaJscode2SessionResult = this.getMaService(appid).jsCode2SessionInfo(code);
+            log.info("【微信授权response】:{}", JSONUtil.toJsonStr(wxMaJscode2SessionResult));
+            WechatOpenBean bean = new WechatOpenBean();
+            bean.setOpenid(wxMaJscode2SessionResult.getOpenid());
+            bean.setUnionid(wxMaJscode2SessionResult.getUnionid());
+            bean.setSessionKey(wxMaJscode2SessionResult.getSessionKey());
+            return bean;
+        } catch (WxErrorException e) {
+            log.error("【微信授权失败】",e);
+            throw new RemoteServiceException("授权失败,请联系相关人员");
+        }
+    }
+
+    /**
+     * 授权手机号
+     * @param phoneCode
+     * @return
+     */
+    public String authMobile(String phoneCode,String appid){
+        String phoneNumber = null;
+        try {
+
+            phoneNumber = this.getMaService(appid).getUserService().getPhoneNoInfo(phoneCode).getPhoneNumber();
+        } catch (WxErrorException e) {
+            log.error("【微信授权手机号失败】",e);
+            throw new RemoteServiceException("授权手机号失败,请联系相关人员");
+        }
+        return phoneNumber ;
+    }
+
+
+
+
+    /**
+     * 获取微信二维码
+     * @param type 二维码类型
+     * @param objId 商品id/券id 之类的
+     * @param userId 生成二维码的用户id
+     * @param companyWechatId 商户id
+     * @return
+     */
+    public String getQrcode(String type,String objId,String userId,String companyWechatId){
+        String param = type + "&" + objId + "&" + userId;
+        WxScene wxScene = new WxScene();
+        wxScene.setParam(param);
+        wxScene.setType(type);
+        wxScene.setCreateTime(new Date());
+        wxSceneService.save(wxScene);
+        return this.genQrCode(wxScene.getSceneId(),companyWechatId);
+    }
+
+
+
+    private String genQrCode(String sceneId,String companyWechatId){
+        try {
+
+            String token = this.getAccessToken();
+
+            Map<String, Object> params = new HashMap<>();
+            params.put("scene", sceneId);  //参数
+            params.put("page", "pages/index/index"); //位置
+            params.put("width", 430);
+
+            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+            HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + token);
+            httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
+            String body = JSONUtil.toJsonStr(params);
+            StringEntity entity;
+            entity = new StringEntity(body);
+            entity.setContentType("image/png");
+
+            httpPost.setEntity(entity);
+            HttpResponse response;
+            response = httpClient.execute(httpPost);
+            InputStream in = response.getEntity().getContent();
+            //流转换为二进制数组,read()是转换方法
+            byte[] data = new byte[1024];
+            int len = 0;
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            while ((len = in.read(data)) != -1) {
+                bos.write(data, 0, len);
+            }
+            bos.close();
+
+            //String baseStr = Base64.getEncoder().encodeToString(bos.toByteArray());
+            long currentTime = System.currentTimeMillis();
+            String filePath = ossUtil.getFilePath()+"/"+ currentTime + UUID.randomUUID().toString() + ".png";
+            ossUtil.uploadFile(filePath, bos.toByteArray());
+
+            return ossUtil.getAccessUrl() + filePath;
+        }catch (Exception e){
+            log.error("获取微信二维码失败:",e);
+        }
+        return null;
+    }
+
+
+    public String getUrlScheme(String query,String companyWechatId) throws IOException, WxErrorException {
+        if(StringUtils.isBlank(companyWechatId)){
+            companyWechatId = Constant.GD_COMPANY_WECHAT_ID;
+        }
+        String accessToken = this.getMaService(companyWechatId).getAccessToken();
+        HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generatescheme?access_token="+accessToken);
+        httpPost.setHeader("Content-Type","application/json; charset=utf-8");
+        httpPost.setConfig(RequestConfig.custom().setConnectTimeout(10*1000).build());
+
+        TreeMap<String,String>  jump_wxa=new TreeMap<>();
+        jump_wxa.put("query",query);
+
+        TreeMap<String,Object> treeMap=new TreeMap<>();
+        treeMap.put("jump_wxa",jump_wxa);
+        httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(treeMap)));
+
+        HttpResponse httpResponse = HttpClientBuilder.create().build().execute(httpPost);
+        String s = EntityUtils.toString(httpResponse.getEntity());
+        if(httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
+            log.info("调用微信接口失败:"+httpPost.getURI());
+            log.info("返回数据:"+s);
+            throw new RuntimeException("调用微信接口失败");
+        }
+        log.info("调用微信接口成功:"+httpPost.getURI());
+        log.info("微信接口返回数据:"+s);
+
+        try{
+            return JSONUtil.parseObj(s).get("openlink").toString();
+        }catch (NullPointerException e){
+            return s;
+        }
+    }
+
+
+    public WxMpQrCodeTicket getMpQrcode(String userId) throws WxErrorException {
+        WxMpService wxMpService = getMpService();
+        if (Objects.isNull(wxMpService)) {
+            throw new RemoteServiceException("配置错误");
+        }
+        WxMpQrcodeService qrcodeService = wxMpService.getQrcodeService();
+        WxMpQrCodeTicket ticket = qrcodeService.qrCodeCreateTmpTicket(userId, 300);
+        return ticket;
+    }
+
+}

+ 31 - 0
src/main/java/com/gree/mall/contest/logic/wxmp/WxMpMessageLogic.java

@@ -0,0 +1,31 @@
+package com.gree.mall.contest.logic.wxmp;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpMessageHandler;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Slf4j
+@Component
+public class WxMpMessageLogic implements WxMpMessageHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
+
+        log.info("事件处理 event={}, toUser={}, fromUser={}, time={}", wxMpXmlMessage.getEvent(), wxMpXmlMessage.getToUser(), wxMpXmlMessage.getFromUser(), wxMpXmlMessage.getCreateTime());
+
+        WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT()
+                .content("测试加密消息")
+                .fromUser(wxMpXmlMessage.getToUser())
+                .toUser(wxMpXmlMessage.getFromUser())
+                .build();
+        return m;
+    }
+}

+ 51 - 0
src/main/java/com/gree/mall/contest/utils/ApplicationContextUtils.java

@@ -0,0 +1,51 @@
+package com.gree.mall.contest.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @Author fcy
+ * @Date 2021/6/11 13:38
+ * @Version 1.0
+ * @Description 全局容器工具
+ */
+public class ApplicationContextUtils {
+
+    private static ApplicationContext applicationContext = null;
+    /**
+     * 获取HttpServletRequest
+     */
+    public static HttpServletRequest getHttpServletRequest() {
+        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+    }
+
+    public static void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        if(ApplicationContextUtils.applicationContext == null){
+            ApplicationContextUtils.applicationContext  = applicationContext;
+        }
+    }
+
+    //获取applicationContext
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    //通过name获取 Bean.
+    public static Object getBean(String name){
+        return getApplicationContext().getBean(name);
+    }
+
+    //通过class获取Bean.
+    public static <T> T getBean(Class<T> clazz){
+        return getApplicationContext().getBean(clazz);
+    }
+
+    //通过name,以及Clazz返回指定的Bean
+    public static <T> T getBean(String name,Class<T> clazz){
+        return getApplicationContext().getBean(name, clazz);
+    }
+}

+ 317 - 0
src/main/java/com/gree/mall/contest/utils/CommonUtils.java

@@ -0,0 +1,317 @@
+package com.gree.mall.contest.utils;
+
+import cn.hutool.core.date.DateUtil;
+import com.gree.mall.contest.exception.RemoteServiceException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.List;
+
+public class CommonUtils {
+
+    public static List<String> initList(List<String> list,int size){
+        int listSize = list.size();
+        if(listSize < size){
+            for(int i = listSize;i < size;i++){
+                list.add("");
+            }
+        }
+        return list;
+    }
+
+    public static List<Object> initList2(List<Object> list,int size){
+        int listSize = list.size();
+        if(listSize < size){
+            for(int i = listSize;i < size;i++){
+                list.add("");
+            }
+        }
+        return list;
+    }
+
+    /**
+     * pc来源的接口环境
+     * @return
+     */
+    public static Boolean isPc(){
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        String appid = request.getHeader("APPID");
+        return StringUtils.isBlank(appid);
+    }
+
+
+    public static String getAPPID(){
+        HttpServletRequest request = RequestContextHolder.getRequestAttributes() != null ? ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() : null;
+        String appid = request.getHeader("APPID");
+        return appid;
+    }
+
+    public static String getUserId() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getUserId(request);
+    }
+
+    public static String getUserId(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            token = request.getParameter("token");
+        }
+        if (StringUtils.isEmpty(token)) {
+            return null;
+        }
+        try {
+            return JwtUtils.parseToken(token).getUserId();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getUsername() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getUsername(request);
+    }
+
+    public static String getUsername(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            return null;
+        }
+        try {
+            return JwtUtils.parseToken(token).getUsername();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getRealName() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getUsername(request);
+    }
+
+    public static String getRealName(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            return null;
+        }
+        try {
+            return JwtUtils.parseToken(token).getRealName();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getUserType() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getUserType(request);
+    }
+
+    public static String getUserType(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            return null;
+        }
+        try {
+            return JwtUtils.parseToken(token).getUserType();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static boolean isExpiration() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return isExpiration(request);
+    }
+
+    public static boolean isExpiration(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            return true;
+        }
+        final Date expiration = JwtUtils.parseToken(token).getExpiration();
+        if (expiration.before(DateUtil.date())) {
+            return true;
+        }
+        return false;
+    }
+
+    public static String getIssuer() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getIssuer(request);
+    }
+
+    public static String getIssuer(HttpServletRequest request) {
+        String token = getToken(request);
+        if (StringUtils.isEmpty(token)) {
+            return null;
+        }
+        try {
+            return JwtUtils.parseToken(token).getIssuer();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getToken() {
+        HttpServletRequest request = ApplicationContextUtils.getHttpServletRequest();
+        return getToken(request);
+    }
+
+    /**
+     * 获取token
+     */
+    public static String getToken(HttpServletRequest request) {
+        String token = request.getHeader("x-token");
+        if (StringUtils.isEmpty(token)) {
+            token = request.getParameter("token");
+        }
+        return token;
+    }
+
+
+    public static void downloadFile(String fileName, HttpServletResponse response) throws IOException {
+        InputStream inputStream = CommonUtils.class.getResourceAsStream("/template/" + fileName);
+        response.setContentType("Content-Type:application/octet-stream");
+        response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
+        OutputStream outputStream = response.getOutputStream();
+        byte[] buffer = new byte[1024];
+        int len;
+        while ((len = inputStream.read(buffer)) != -1) {
+            outputStream.write(buffer, 0, len);
+        }
+        inputStream.close();
+        outputStream.close();
+    }
+
+
+    /**
+     * 组装服务单修改记录的日志
+     *
+     * @return
+     */
+    public static String supplyLog(String title, Object oldValue, Object newValue) {
+        if (oldValue instanceof String) {
+            oldValue = oldValue == null ? "" : oldValue;
+            newValue = newValue == null ? "" : newValue;
+            if (!StringUtils.equals((String) oldValue, (String) newValue)) {
+                return "【" + title + "】修改前:" + oldValue + ",修改后:" + newValue + "。";
+            }
+        }
+        if (oldValue instanceof Integer) {
+            oldValue = oldValue == null ? 0 : oldValue;
+            newValue = newValue == null ? 0 : newValue;
+            if (!((Integer) oldValue).equals((Integer) newValue)) {
+                return "【" + title + "】修改前:" + oldValue + ",修改后:" + newValue + "。";
+            }
+        }
+        if(oldValue instanceof Date){
+            long o = oldValue == null ? 0 : ((Date) oldValue).getTime();
+            long n = newValue == null ? 0 : ((Date) newValue).getTime();
+            if(o != n){
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                return "【" + title + "】修改前:" + sdf.format(oldValue) + ",修改后:" + sdf.format(newValue) + "。";
+            }
+        }
+        if(oldValue == null && newValue != null){
+            oldValue = oldValue == null ? "" : oldValue;
+            newValue = newValue == null ? "" : newValue;
+            return "【" + title + "】修改前:" + oldValue + ",修改后:" + newValue + "。";
+
+        }
+        if(oldValue != null && newValue == null){
+            oldValue = oldValue == null ? "" : oldValue;
+            newValue = newValue == null ? "" : newValue;
+            return "【" + title + "】修改前:" + oldValue + ",修改后:" + newValue + "。";
+        }
+        return "";
+    }
+
+
+
+
+
+    /**
+     * 根据URL地址获取文件
+     * @param path URL网络地址
+     * @return File
+     */
+    public static File getFileByHttpURL(String path, String address){
+        String newUrl = path.split("[?]")[0];
+        String[] suffix = newUrl.split("/");
+        //得到最后一个分隔符后的名字
+        String fileName = suffix[suffix.length - 1];
+        File file = null;
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try{
+            file = new File(address+"/"+fileName);//创建临时文件
+            URL urlFile = new URL(path);
+            inputStream = urlFile.openStream();
+            outputStream = new FileOutputStream(file);
+
+            int bytesRead = 0;
+            byte[] buffer = new byte[8192];
+            while ((bytesRead=inputStream.read(buffer,0,8192))!=-1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+        }catch (Exception e) {
+            throw new RemoteServiceException(e.getMessage());
+        }finally {
+            try {
+                if (null != outputStream) {
+                    outputStream.close();
+                }
+                if (null != inputStream) {
+                    inputStream.close();
+                }
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return file;
+    }
+
+
+
+    /**
+     * 计算两个时间段的工作时长
+     * @param startTime 工作开始时间
+     * @param endTime 工作结束时间
+     * @param taskStartTime 任务开始时间
+     * @param taskEndTime 任务结束时间
+     * @return
+     */
+    public static long calculateValidWorkTime(LocalTime startTime, LocalTime endTime, LocalTime taskStartTime, LocalTime taskEndTime) {
+        long totalMills = 0;
+
+        // 任务开始是否在工作时间内
+        if (taskStartTime.isAfter(startTime) && taskStartTime.isBefore(endTime)) {
+            // 任务结束是否在工作时间内
+            if (taskEndTime.isAfter(startTime) && taskEndTime.isBefore(endTime)) {
+                // 任务全部在有效工作时间内
+                totalMills = ChronoUnit.MILLIS.between(taskStartTime, taskEndTime);
+            } else {
+                // 任务从工作时间开始到结束不完全在有效工作时间内
+                totalMills = ChronoUnit.MILLIS.between(taskStartTime, endTime);
+            }
+        } else {
+            // 任务开始时间超出工作时间
+            LocalTime nextWorkStart = startTime.isAfter(taskStartTime) ? startTime : taskStartTime;
+            LocalTime nextWorkEnd = endTime.isAfter(taskEndTime) ? taskEndTime : endTime;
+            totalMills = ChronoUnit.MILLIS.between(nextWorkStart, nextWorkEnd);
+        }
+
+        return totalMills;
+    }
+
+}

+ 46 - 0
src/main/java/com/gree/mall/contest/utils/IpUtil.java

@@ -0,0 +1,46 @@
+package com.gree.mall.contest.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @Author: duke
+ * @Date: 2018/2/23 下午2:28
+ */
+public class IpUtil {
+    public static String getIpAddr(HttpServletRequest request) {
+        String ipAddress = null;
+        try {
+            ipAddress = request.getHeader("x-forwarded-for");
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getHeader("Proxy-Client-IP");
+            }
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+                ipAddress = request.getRemoteAddr();
+                if (ipAddress.equals("127.0.0.1")) {
+                    InetAddress inet = null;
+                    try {
+                        inet = InetAddress.getLocalHost();
+                    } catch (UnknownHostException e) {
+                        e.printStackTrace();
+                    }
+                    ipAddress = inet.getHostAddress();
+                }
+            }
+            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
+            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
+                // = 15
+                if (ipAddress.indexOf(",") > 0) {
+                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
+                }
+            }
+        } catch (Exception e) {
+            ipAddress = "";
+        }
+        return ipAddress;
+    }
+}

+ 193 - 0
src/main/java/com/gree/mall/contest/utils/JwtUtils.java

@@ -0,0 +1,193 @@
+package com.gree.mall.contest.utils;
+
+import com.gree.mall.contest.constant.Constant;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JwtUtils {
+    // 配置参数(可从配置中心读取)
+//    private static final String SECRET = "zfire@2025_base_code_security_key_123456"; // 64位HMAC-SHA512密钥
+//    private static final long EXPIRATION = 7200L; // 默认有效期2小时(秒)
+//    private static final String ISSUER = "base-code-system"; // 签发者
+    // HMAC-SHA512签名算法
+//    private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(SECRET.getBytes());
+
+    // 从配置中心读取参数(示例从application.yml读取)
+    @Getter
+    private static String secret;
+    @Getter
+    private static Long expiration;
+
+    private static SecretKey secretKey;
+
+    @Value("${jwt.secret}")
+    public void setSecret(String secret) {
+        JwtUtils.secret = secret;
+        secretKey = Keys.hmacShaKeyFor(secret.getBytes());
+    }
+
+    @Value("${jwt.expiration}")
+    public void setExpiration(Long expiration) {
+        JwtUtils.expiration = expiration;
+    }
+
+
+    /**
+     * 生成JWT令牌
+     * @param userId    用户ID
+     * @param username  用户名
+     * @param realName  真实姓名
+     * @param userType 用户类型
+     * @param issuer 用户角色
+     * @return JWT令牌
+     */
+    public static String generateToken(String userId, String username, String realName, String userType, String issuer) {
+        Map<String, Object> claims = new HashMap<>(3);
+        claims.put(Constant.CLAIM_USER_ID, userId);
+        claims.put(Constant.CLAIM_USERNAME, username);
+        claims.put(Constant.CLAIM_REAL_NAME, realName);
+        claims.put(Constant.CLAIM_USER_TYPE, userType);
+
+        return Jwts.builder()
+                .issuer(issuer)
+                .subject(username)
+                .claims(claims)
+                .issuedAt(new Date())
+                .expiration(new Date(System.currentTimeMillis() + expiration * 1000))
+                .signWith(secretKey, Jwts.SIG.HS512)
+                .compact();
+    }
+
+    /**
+     * 解析JWT令牌
+     * @param token JWT令牌
+     * @return 解析结果包装对象
+     */
+    public static JwtParseResult parseToken(String token) {
+        try {
+            Claims claims = Jwts.parser()
+                    .verifyWith(secretKey)
+                    .build()
+                    .parseSignedClaims(token)
+                    .getPayload();
+
+            return JwtParseResult.success()
+                    .userId(claims.get(Constant.CLAIM_USER_ID, String.class))
+                    .username(claims.getSubject())
+                    .realName(claims.get(Constant.CLAIM_REAL_NAME, String.class))
+                    .userType(claims.get(Constant.CLAIM_USER_TYPE, String.class))
+                    .expiration(claims.getExpiration())
+                    .issuer(claims.getIssuer());
+        } catch (ExpiredJwtException e) {
+            return JwtParseResult.fail(1001, "令牌已过期");
+        } catch (SignatureException e) {
+            return JwtParseResult.fail(1002, "无效签名");
+        } catch (Exception e) {
+            return JwtParseResult.fail(1003, "令牌解析失败");
+        }
+    }
+
+
+
+    /**
+     * 刷新令牌
+     * @param token 原令牌
+     * @return 新令牌(仅当原令牌有效且未过期时刷新)
+     */
+    public static String refreshToken(String token, String issuer) {
+        JwtParseResult result = parseToken(token);
+        if (!result.isSuccess()) {
+            return null;
+        }
+        return generateToken(
+                result.getUserId(),
+                result.getUsername(),
+                result.getRealName(),
+                result.getUserType(),
+                issuer
+        );
+    }
+
+    /**
+     * JWT解析结果包装类
+     */
+    public static class JwtParseResult {
+        private boolean success;
+        private int code;
+        private String message;
+        private String userId;
+        private String username;
+        private String realName;
+        private String userType;
+        private Date expiration;
+        private String issuer;
+
+        public static JwtParseResult success() {
+            JwtParseResult result = new JwtParseResult();
+            result.success = true;
+            result.code = 200;
+            result.message = "success";
+            return result;
+        }
+
+        public static JwtParseResult fail(int code, String message) {
+            JwtParseResult result = new JwtParseResult();
+            result.success = false;
+            result.code = code;
+            result.message = message;
+            return result;
+        }
+
+        // 链式调用设置字段
+        public JwtParseResult userId(String userId) {
+            this.userId = userId;
+            return this;
+        }
+
+        public JwtParseResult username(String username) {
+            this.username = username;
+            return this;
+        }
+
+        public JwtParseResult realName(String realName) {
+            this.realName = realName;
+            return this;
+        }
+
+        public JwtParseResult userType(String userType) {
+            this.userType = userType;
+            return this;
+        }
+
+        public JwtParseResult expiration(Date expiration) {
+            this.expiration = expiration;
+            return this;
+        }
+
+        public JwtParseResult issuer(String issuer) {
+            this.issuer = issuer;
+            return this;
+        }
+
+        // Getter 方法
+        public boolean isSuccess() { return success; }
+        public int getCode() { return code; }
+        public String getMessage() { return message; }
+        public String getUserId() { return userId; }
+        public String getUsername() { return username; }
+        public String getRealName() { return realName; }
+        public String getUserType() { return userType; }
+        public Date getExpiration() { return expiration; }
+        public String getIssuer() { return issuer; }
+    }
+}

+ 608 - 0
src/main/java/com/gree/mall/contest/utils/RedisUtil.java

@@ -0,0 +1,608 @@
+package com.gree.mall.contest.utils;
+
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 单机版redis工具类
+ * 对应/redis/RedisConfig.java
+ */
+public class RedisUtil {
+
+
+    private RedisTemplate<String, Object> redisTemplate;
+
+
+    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+    //=============================common============================
+    /**
+     * 指定缓存失效时间
+     * @param key 键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key,long time){
+        try {
+            if(time>0){
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key){
+        return redisTemplate.getExpire(key,TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key){
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String ... key){
+        if(key!=null&&key.length>0){
+            if(key.length==1){
+                redisTemplate.delete(key[0]);
+            }else{
+                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    //============================String=============================
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key){
+        return key==null?null:redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key 键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key,Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key,Object value,long time){
+        try {
+            if(time>0){
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            }else{
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     * @param key 键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta){
+        if(delta<0){
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     * @param key 键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta){
+        if(delta<0){
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    //================================Map=================================
+    /**
+     * HashGet
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key,String item){
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object,Object> hmget(String key){
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String,Object> map){
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     * @param key 键
+     * @param map 对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String,Object> map, long time){
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if(time>0){
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key,String item,Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key,String item,Object value,long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if(time>0){
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     * @param key 键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item){
+        redisTemplate.opsForHash().delete(key,item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item){
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key 键
+     * @param item 项
+     * @param by 要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item,double by){
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key 键
+     * @param item 项
+     * @param by 要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item,double by){
+        return redisTemplate.opsForHash().increment(key, item,-by);
+    }
+
+    //============================set=============================
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key){
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key,Object value){
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object...values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     * @param key 键
+     * @param time 时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key,long time,Object...values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if(time>0) expire(key, time);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(String key){
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object ...values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    //===============================list=================================
+
+    /**
+     * 获取list缓存的内容
+     * @param key 键
+     * @param start 开始
+     * @param end 结束  0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> lGet(String key,long start, long end){
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public List<Object> lGetAll(String key){
+        try {
+            return redisTemplate.opsForList().range(key, 0, this.lGetListSize(key));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long lGetListSize(String key){
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     * @param key 键
+     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object lGetIndex(String key,long index){
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key 键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index,Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     * @param key 键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key,long count,Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+
+    /**
+     * 使用setNX的形式锁
+     * @param key
+     * @param second
+     * @return
+     */
+    public boolean lock(String key,final Long second){
+        //final String lock = LOCK_PREFIX + key;
+        Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, "lock", second, TimeUnit.SECONDS);
+        //String requestId = requestId();//解铃还需系铃人
+        return lock;
+    }
+
+    public String requestId(){
+        return UUID.randomUUID().toString();
+    }
+
+
+    public boolean unLock(String key) {
+        del(key);
+        return true;
+    }
+
+
+    /**
+     * 解锁还须加索人(MVP25版本以后用)
+     * @param key   加索的key
+     * @param requestId  加索人的密码(后续通过该参数进行解锁)
+     * @param second 加索的时间(秒)
+     * @return
+     */
+//    public boolean lock(String key,String requestId ,final Long second){
+//        //final String lock = LOCK_PREFIX + key;
+//        String result = jedisCluster.set(key, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, second*1000);
+//
+//        if (LOCK_SUCCESS.equals(result)) {
+//            return true;
+//        }
+//        return false;
+//    }
+
+    /**
+     * 解锁还需系锁人,后续版本使用该种解锁方法(MVP25版本后用)
+     * @return
+     */
+//    public boolean unLock(String lockKey,String requestId) {
+//        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+//        Object result = jedisCluster.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
+//        if (RELEASE_SUCCESS.equals(result)) {
+//            return true;
+//        }
+//        return false;
+//    }
+
+
+    public String rPop(String key){
+        String str = (String)redisTemplate.opsForList().rightPop(key);
+        return str;
+    }
+
+    public boolean lPush(String key,String value){
+        redisTemplate.opsForList().leftPush(key,value);
+        return true;
+    }
+
+
+
+}
+

+ 244 - 0
src/main/java/com/gree/mall/contest/utils/VerifiUtils.java

@@ -0,0 +1,244 @@
+package com.gree.mall.contest.utils;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.FileImageOutputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+@Slf4j
+public class VerifiUtils {
+
+    final static int targetWidth = 55;//小图长
+    final static int targetHeight = 45;//小图宽
+    final static int circleR = 8;//半径
+    final static int r1 = 4;//距离点
+
+
+    public static Map<String,Object> getVerifi(){
+        Map<String, Object> resultMap = new HashMap<>();
+        //读取本地路径下的图片,随机选一条
+        int i = new Random().nextInt(5) + 1;
+        URL resource = VerifiUtils.class.getClassLoader().getResource("static/"+i+".png");
+//        File file = new File(VerifiUtils.class.getResource("/static").getPath());
+//        File[] files = file.listFiles();
+//        int n = new Random().nextInt(files.length)+1;
+        VerifiUtils.createImage(resource, resultMap);
+        log.info("【拖拽式验证码】值:{}",resultMap.get("xWidth"));
+        //读取网络图片
+        resultMap.put("errcode", 0);
+        resultMap.put("errmsg", "success");
+        return resultMap;
+    }
+
+    //byte数组到图片
+    public static void byte2image(byte[] data,String path){
+        if(data.length<3||path.equals("")) return;
+        try{
+            FileImageOutputStream imageOutput = new FileImageOutputStream(new File(path));
+            imageOutput.write(data, 0, data.length);
+            imageOutput.close();
+        } catch(Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    /**
+     * @Createdate: 2019年1月24日上午10:52:42
+     * @Title: getBlockData
+     * @Description: 生成小图轮廓
+     * @author zhoujin
+     * @return int[][]
+     * @throws
+     */
+    private static int[][] getBlockData() {
+        int[][] data = new int[targetWidth][targetHeight];
+        double x2 = targetWidth -circleR; //47
+
+        //随机生成圆的位置
+        double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);
+        double po = Math.pow(circleR,2); //64
+
+        double xbegin = targetWidth - circleR - r1;
+        double ybegin = targetHeight- circleR - r1;
+
+        //圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆
+        //计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色
+        for (int i = 0; i < targetWidth; i++) {
+            for (int j = 0; j < targetHeight; j++) {
+                double d2 = Math.pow(j - 2,2) + Math.pow(i - h1,2);
+                double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);
+//                if ((j == ybegin && d2 < po)||(i == xbegin && d3 > po)) {
+//                    data[i][j] = 2;
+//                   continue;
+//                }
+                if ((j <= ybegin && d2 < po)||(i >= xbegin && d3 > po)) {
+                    data[i][j] = 0;
+                }  else {
+                    data[i][j] = 1;
+                }
+            }
+        }
+        return data;
+    }
+
+    /**
+     *
+     * @Createdate: 2019年1月24日上午10:51:30
+     * @Title: cutByTemplate
+     * @Description: 有这个轮廓后就可以依据这个二维数组的值来判定抠图并在原图上抠图位置处加阴影,
+     * @author zhoujin
+     * @param oriImage  原图
+     * @param targetImage  抠图拼图
+     * @param templateImage 颜色
+     * @param x
+     * @param y void
+     * @throws
+     */
+    private static void cutByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] templateImage, int x, int y){
+        int[][] martrix = new int[3][3];
+        int[] values = new int[9];
+        //创建shape区域
+        for (int i = 0; i < targetWidth; i++) {
+            for (int j = 0; j < targetHeight; j++) {
+                int rgb = templateImage[i][j];
+                // 原图中对应位置变色处理
+                int rgb_ori = oriImage.getRGB(x + i, y + j);
+
+                if (rgb == 1) {
+                    targetImage.setRGB(i, j, rgb_ori);
+
+                    //抠图区域高斯模糊
+                    readPixel(oriImage, x + i, y + j, values);
+                    fillMatrix(martrix, values);
+                    oriImage.setRGB(x + i, y + j, avgMatrix(martrix));
+                }else if(rgb == 2){
+                    //Graphics graphics = targetImage.createGraphics();
+                    Color myWhite = new Color(255, 255, 255); // Color white
+                    targetImage.setRGB(i,j,myWhite.getRGB());
+                }else{
+                    //这里把背景设为透明
+                    targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
+                }
+            }
+        }
+    }
+
+
+    private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
+        int xStart = x - 1;
+        int yStart = y - 1;
+        int current = 0;
+        for (int i = xStart; i < 3 + xStart; i++)
+            for (int j = yStart; j < 3 + yStart; j++) {
+                int tx = i;
+                if (tx < 0) {
+                    tx = -tx;
+
+                } else if (tx >= img.getWidth()) {
+                    tx = x;
+                }
+                int ty = j;
+                if (ty < 0) {
+                    ty = -ty;
+                } else if (ty >= img.getHeight()) {
+                    ty = y;
+                }
+                pixels[current++] = img.getRGB(tx, ty);
+
+            }
+    }
+
+    private static void fillMatrix(int[][] matrix, int[] values) {
+        int filled = 0;
+        for (int i = 0; i < matrix.length; i++) {
+            int[] x = matrix[i];
+            for (int j = 0; j < x.length; j++) {
+                x[j] = values[filled++];
+            }
+        }
+    }
+
+    private static int avgMatrix(int[][] matrix) {
+        int r = 0;
+        int g = 0;
+        int b = 0;
+        for (int i = 0; i < matrix.length; i++) {
+            int[] x = matrix[i];
+            for (int j = 0; j < x.length; j++) {
+                if (j == 1) {
+                    continue;
+                }
+                Color c = new Color(x[j]);
+                r += c.getRed();
+                g += c.getGreen();
+                b += c.getBlue();
+            }
+        }
+        return new Color(r / 8, g / 8, b / 8).getRGB();
+    }
+
+    /**
+     * @Description: 读取网络图片,生成拼图验证码
+     * @author zhoujin
+     * @return Map<String,Object>  返回生成的抠图和带抠图阴影的大图 base64码及抠图坐标
+     */
+    public static Map<String,Object> createImage(URL url, Map<String,Object> resultMap){
+        try {
+            //通过URL 读取图片
+            //URL url = new URL(imgUrl);
+            BufferedImage bufferedImage = ImageIO.read(url.openStream());
+            Random rand = new Random();
+            int widthRandom = rand.nextInt(bufferedImage.getWidth()-  targetWidth - 100 + 1 ) + 100;
+            int heightRandom = rand.nextInt(bufferedImage.getHeight()- targetHeight + 1 );
+            log.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})",bufferedImage.getWidth(),bufferedImage.getHeight(),widthRandom,heightRandom);
+            BufferedImage targetImage= new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_4BYTE_ABGR);
+            cutByTemplate(bufferedImage,targetImage,getBlockData(),widthRandom,heightRandom);
+            //给小图画边框
+//            Graphics graphics = targetImage.createGraphics();
+//            graphics.drawImage(targetImage, 0, 0, targetWidth, targetHeight, null); // 绘制图
+//            graphics.setColor(Color.WHITE);
+//            graphics.drawRect(0, 0, widthRandom - 1, heightRandom - 1);
+//            graphics.drawRect(1, 1, widthRandom - 1, heightRandom - 1);
+//            graphics.drawRect(0, 0, widthRandom - 2, heightRandom - 2);
+            resultMap.put("bigImage", getImageBASE64(bufferedImage));//大图
+            resultMap.put("smallImage", getImageBASE64(targetImage));//小图
+            resultMap.put("xWidth",widthRandom);
+            resultMap.put("yHeight",heightRandom);
+        } catch (Exception e) {
+            log.info("创建图形验证码异常",e);
+        } finally{
+            return resultMap;
+        }
+    }
+
+
+    /**
+     * @Title: getImageBASE64
+     * @Description: 图片转BASE64
+     * @author zhoujin
+     * @param image
+     * @return
+     */
+    public static String getImageBASE64(BufferedImage image) throws IOException {
+        byte[] imagedata = null;
+        ByteArrayOutputStream bao=new ByteArrayOutputStream();
+        ImageIO.write(image,"png",bao);
+        imagedata=bao.toByteArray();
+        //String BASE64IMAGE=Base64Utils.encode(imagedata);
+        String BASE64IMAGE=Base64.getEncoder().encodeToString(imagedata);
+        BASE64IMAGE = BASE64IMAGE.replaceAll("\r|\n", "");  //删除 \r\n
+        //byte2image(imagedata,UUID.randomUUID()+".jpg");
+        return BASE64IMAGE;
+    }
+
+}

+ 190 - 0
src/main/java/com/gree/mall/contest/utils/oss/OSSUtil.java

@@ -0,0 +1,190 @@
+package com.gree.mall.contest.utils.oss;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.common.utils.BinaryUtil;
+import com.aliyun.oss.model.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+@Service
+@Slf4j
+public class OSSUtil {
+
+    @Value("${ali.access.key.id}")
+    private String ACCESS_KEY_ID;
+    @Value("${ali.access.key.secert}")
+    private String ACCESS_KEY_SECERT;
+
+    @Value("${ali.oss.bucket.name}")
+    private String OSS_BUCKET_NAME;
+    @Value("${ali.oss.endpoint.ww}")
+    private String OSS_ENDPOINT_WW;
+    @Value("${ali.oss.endpoint}")
+    private String OSS_ENDPOINT;
+    @Value("${spring.profiles.active}")
+    private String active;
+    @Value("${sys.url}")
+    private String sysUrl;
+
+//    public String getAccessUrl(){
+//        return "https://" + OSS_BUCKET_NAME +"."+ OSS_ENDPOINT_WW;
+//    }
+
+    public String getAccessUrl(){
+        return sysUrl + "/img/get?key=";
+    }
+
+    public String getFilePath() {
+        String month = new SimpleDateFormat("yyyy-MM").format(new Date());
+        return month;
+    }
+
+    public OSS buildClient(){
+        //        return new OSSClientBuilder().build(OSS_ENDPOINT_WW, ACCESS_KEY_ID, ACCESS_KEY_SECERT);
+        return new OSSClientBuilder().build(!active.equals("prd") ? OSS_ENDPOINT_WW : OSS_ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECERT);
+    }
+
+    /**
+     * 上传到oss
+     *
+     * @param ossObjectKey 附件的唯一路径名称
+     * @param file         附件
+     * @return
+     */
+    public boolean uploadFile(String ossObjectKey, File file) {
+        PutObjectResult putObjectResult = buildClient().putObject(new PutObjectRequest(OSS_BUCKET_NAME, ossObjectKey, file));
+        log.info("putObjectResult" + putObjectResult);
+        return true;
+    }
+
+    public boolean uploadFile(String ossObjectKey, byte[] file) {
+        PutObjectResult putObjectResult = buildClient().putObject(new PutObjectRequest(OSS_BUCKET_NAME, ossObjectKey, new ByteArrayInputStream(file)));
+        log.info("putObjectResult" + putObjectResult);
+        return true;
+    }
+
+    public boolean uploadFile(String ossObjectKey, ByteArrayInputStream byteArrayInputStream) {
+        PutObjectResult putObjectResult = buildClient().putObject(new PutObjectRequest(OSS_BUCKET_NAME, ossObjectKey, byteArrayInputStream));
+        log.info("putObjectResult" + putObjectResult);
+        return true;
+    }
+
+    public boolean uploadFile(String ossObjectKey, InputStream inputStream) {
+        PutObjectResult putObjectResult = buildClient().putObject(new PutObjectRequest(OSS_BUCKET_NAME, ossObjectKey, inputStream));
+        log.info("putObjectResult" + putObjectResult);
+        return true;
+    }
+
+
+    public boolean uploadFile(String ossObjectKey, InputStream inputStream, Long second) {
+        PutObjectRequest putObjectRequest = new PutObjectRequest(OSS_BUCKET_NAME, ossObjectKey, inputStream);
+        //文件过期时间
+        putObjectRequest.addHeader("x-oss-expires", second + "");
+        PutObjectResult putObjectResult = buildClient().putObject(putObjectRequest);
+        log.info("putObjectResult" + putObjectResult);
+        return true;
+    }
+
+
+    public Map<String, String> getConfig() throws UnsupportedEncodingException {
+        OSS client = buildClient();
+        String host = "https://" + OSS_BUCKET_NAME + "." + OSS_ENDPOINT_WW; // host的格式为 bucketname.endpoint
+
+//        OSSClient client = new OSSClient(endpoint, accessId, accessKey);
+        long expireTime = 300;
+        long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
+        java.sql.Date expiration = new java.sql.Date(expireEndTime);
+        PolicyConditions policyConds = new PolicyConditions();
+        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
+        policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, getFilePath());
+
+        String postPolicy = client.generatePostPolicy(expiration, policyConds);
+        byte[] binaryData = postPolicy.getBytes("utf-8");
+        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
+        String postSignature = client.calculatePostSignature(postPolicy);
+
+        Map<String, String> respMap = new LinkedHashMap<String, String>();
+        respMap.put("OSSAccessKeyId", ACCESS_KEY_ID);
+        respMap.put("expire", String.valueOf(expireEndTime / 1000));
+        respMap.put("policy", encodedPolicy);
+        respMap.put("signature", postSignature);
+        respMap.put("id", ACCESS_KEY_ID);
+        respMap.put("dir", getFilePath()+"/");
+        respMap.put("host", host);
+        return respMap;
+    }
+
+
+    public String getUrl(String key) {
+        if (StringUtils.isEmpty(key)) {
+            return "";
+        }
+        // 设置URL过期时间为1小时
+        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+        GeneratePresignedUrlRequest generatePresignedUrlRequest;
+        generatePresignedUrlRequest = new GeneratePresignedUrlRequest(OSS_BUCKET_NAME, key);
+        generatePresignedUrlRequest.setExpiration(expiration);
+        URL url = buildClient().generatePresignedUrl(generatePresignedUrlRequest);
+        return url.toString();
+    }
+
+    public URL getUrl2(String key) {
+        // 设置URL过期时间为1小时
+        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+        GeneratePresignedUrlRequest generatePresignedUrlRequest;
+        generatePresignedUrlRequest = new GeneratePresignedUrlRequest(OSS_BUCKET_NAME, key);
+        generatePresignedUrlRequest.setExpiration(expiration);
+        return buildClient().generatePresignedUrl(generatePresignedUrlRequest);
+    }
+
+
+    public String getUrlWw(String key) {
+        return getUrlWw(key, null);
+    }
+
+    public String getUrlWw(String key, MediaType mediaType) {
+        if (StringUtils.isEmpty(key)) {
+            return "";
+        }
+        // 设置URL过期时间为1小时
+        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
+        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(OSS_BUCKET_NAME, key);
+        request.setExpiration(expiration);
+        if (mediaType != null) {
+            request.setContentType(mediaType.toString());
+        }
+
+        URL url = new OSSClientBuilder()
+                .build("https://" + OSS_ENDPOINT_WW, ACCESS_KEY_ID, ACCESS_KEY_SECERT)
+                .generatePresignedUrl(request);
+
+        String resultUrl = url.toString();
+
+        return resultUrl;
+    }
+
+
+    public boolean imageHandle(String ossObjectKey, String style, String savePath) {
+        OSS client = new OSSClientBuilder().build(OSS_ENDPOINT_WW, ACCESS_KEY_ID, ACCESS_KEY_SECERT);
+        GetObjectRequest getObjectRequest = new GetObjectRequest(OSS_BUCKET_NAME, ossObjectKey);
+        getObjectRequest.setProcess(style);
+        log.info("getObjectRequest: {}", getObjectRequest);
+        client.getObject(getObjectRequest, new File(savePath));
+        return true;
+    }
+}

+ 466 - 0
src/main/java/com/gree/mall/contest/utils/zfire/FieldUtils.java

@@ -0,0 +1,466 @@
+package com.gree.mall.contest.utils.zfire;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.util.BooleanUtils;
+import com.alibaba.excel.write.handler.CellWriteHandler;
+import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.gree.mall.contest.annotation.ZfireField;
+import com.gree.mall.contest.bean.ExcelData;
+import com.gree.mall.contest.bean.admin.AdminUserCom;
+import com.gree.mall.contest.bean.zfire.QueryParamBean;
+import com.gree.mall.contest.bean.zfire.ZfireParamBean;
+import com.gree.mall.contest.constant.Constant;
+import com.gree.mall.contest.plus.entity.AdminField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.springframework.beans.BeansException;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+@Slf4j
+public class FieldUtils {
+
+
+    public void test() throws NoSuchFieldException, IllegalAccessException {
+//        QueryParamBean map = new QueryParamBean();
+//        map.setCompare("like");
+//        map.setParam("name");
+//        map.setValue("123");
+//
+//
+//        QueryParamBean map2 = new QueryParamBean();
+//        map2.setCompare("=");
+//        map2.setParam("phone");
+//        map2.setValue("1234");
+//        //map2.setCreateTime(new Date());
+//
+//        Field[] declaredFields = map2.getClass().getDeclaredFields();
+//
+//        Field param = map2.getClass().getDeclaredField("param");
+//        param.setAccessible(true);
+//        Object o = param.get(map2);
+//
+//        Field createTime = map2.getClass().getDeclaredField("createTime");
+//        createTime.setAccessible(true);
+//        Object o2 = createTime.get(map2);
+//        String typeName = createTime.getType().getTypeName();
+//
+//        String s = this.supplyParam(Arrays.asList(map, map2));
+//        System.out.print(s);
+    }
+
+
+    /**
+     * 组装最终的sql条件和排序
+     * @param bean
+     * @return
+     */
+    public static ZfireParamBean supplyParam(ZfireParamBean bean){
+        return supplyParam(bean,null);
+    }
+
+    public static ZfireParamBean supplyParam(ZfireParamBean bean,Class cls){
+        //限制最多查询10w条
+        if(bean.getPageSize() != null && (bean.getPageSize().equals(-1) || bean.getPageSize().intValue() > 100000)){
+            bean.setPageSize(Constant.PAGE_SIZE);
+        }
+        //todo 框架自带防注入
+        bean.setQuery(supplyParam(bean.getParams()));
+        if(StringUtils.isNotBlank(bean.getCompanyWechatId())){
+            bean.setQuery(bean.getQuery() + " and a.company_wechat_id = '"+bean.getCompanyWechatId()+"'" );
+        }
+        if(bean.getClazzType() != null){
+            bean.setSelected(buildSelectColumn(bean.getClazzType()));
+        }else if(cls != null) {
+            bean.setSelected(buildSelectColumn(cls));
+        }
+        if(StringUtils.isNotBlank(bean.getOrderBy()) && !bean.getOrderBy().contains("order by")) {
+            bean.setOrderBy("order by " + bean.getOrderBy());
+        }
+        return bean;
+    }
+    public static ZfireParamBean supplyParam(ZfireParamBean bean, Class cls, AdminUserCom adminUser){
+     /*   if (Objects.nonNull(adminUser.getAdminCompanyWechat())) {
+            bean.setCompanyWechatId(adminUser.getAdminCompanyWechat().getCompanyWechatId());
+        }*/
+        if (Objects.nonNull(adminUser)) {
+            bean.setAdminWebsitIds(adminUser.getAdminWebsitIds());
+        }
+        //限制最多查询10w条
+        if(bean.getPageSize() != null && (bean.getPageSize().equals(-1) || bean.getPageSize().intValue() > 100000)){
+            bean.setPageSize(100000);
+        }
+        //todo 框架自带防注入
+        bean.setQuery(supplyParam(bean.getParams()));
+        if(StringUtils.isNotBlank(bean.getCompanyWechatId())){
+            bean.setQuery(bean.getQuery() + " and a.company_wechat_id = '"+bean.getCompanyWechatId()+"'" );
+        }
+        if(bean.getClazzType() != null){
+            bean.setSelected(buildSelectColumn(bean.getClazzType()));
+        }else if(cls != null) {
+            bean.setSelected(buildSelectColumn(cls));
+        }
+        if(StringUtils.isNotBlank(bean.getOrderBy()) && !bean.getOrderBy().contains("order by")) {
+            bean.setOrderBy("order by " + bean.getOrderBy());
+        }
+        return bean;
+    }
+
+    /**
+     * 组装最终的sql查询字段
+     * @param clazz
+     * @return
+     */
+    private static String buildSelectColumn(Class clazz) {
+        if (Objects.isNull(clazz)) {
+            return "*";
+        }
+        String defaultTbName = Optional.of(clazz)
+                .filter(item -> item.isAnnotationPresent(ZfireField.class))
+                .map(item -> item.getAnnotation(ZfireField.class))
+                .map(ZfireField.class::cast)
+                .map(ZfireField::tbName)
+                .orElse("");
+
+        Field[] fields = clazz.getDeclaredFields();
+        List<String> sqlNameList = Lists.newArrayList();
+        for (Field field : fields) {
+            ZfireField annotation = field.getAnnotation(ZfireField.class);
+            if (annotation != null && annotation.ignoreSelect()) {
+                continue;
+            }
+
+            String fieldTbName = Optional.ofNullable(annotation)
+                    .map(ZfireField::tbName)
+                    .filter(StringUtils::isNotBlank)
+                    .orElse(defaultTbName);
+//            String sqlName = Optional.ofNullable(annotation)
+//                    .map(ZfireField::colName)
+//                    .filter(StringUtils::isNotBlank)
+//                    .orElseGet(() -> parseLineColName(field.getName(), fieldTbName));
+            String sqlName = parseLineColName(field.getName(),fieldTbName);
+            sqlNameList.add(sqlName);
+        }
+        if (CollectionUtils.isEmpty(sqlNameList)) {
+            return "*";
+        }
+        return Joiner.on(",")
+                .skipNulls()
+                .join(sqlNameList);
+    }
+
+    @NotNull
+    private static String parseLineColName(String name, String fieldTbName) {
+        String lineName = name.replaceAll("[A-Z]", "_" + "$0").toLowerCase();
+        String sqlName = StringUtils.isNotBlank(fieldTbName) ? fieldTbName + "." + lineName : lineName;
+        return sqlName;
+    }
+
+
+    /**
+     *
+     * 根据前端传的集合生成查询条件
+     * [{
+     *    "param":"条件名称",
+     *    "compare":"比较符(><=like)"
+     *    "value":"内容"
+     * }]
+     * @return
+     */
+    public static String supplyParam(List<QueryParamBean> params){
+        StringBuffer sb = new StringBuffer(" where 1=1 ");
+        if(params == null || params.size() == 0){
+            return sb.toString();
+        }
+
+        for(QueryParamBean paramBean : params){
+            if(paramBean.getValue() == null)
+                continue;
+            if(paramBean.getValue() instanceof  ArrayList){
+                List<String> values = (List<String>) paramBean.getValue();
+                if(values != null && values.size() > 0) {
+                    String join = "";
+                    for(String s : values){
+                        s = replaceValue(s);
+                        join += "'"+s+"',";
+                    }
+                    join = join.substring(0,join.length() - 1);
+                    sb.append("and ").append(paramBean.getParam()).append(" in(").append(join).append(") ");
+                }
+            }else {
+                String value = "";
+                if(paramBean.getValue() instanceof Boolean){
+                    value = paramBean.getValue().toString();
+                } else {
+                    value = paramBean.getValue().toString();
+                }
+                if(StringUtils.isBlank(value))
+                    continue;
+
+                value = replaceValue(value);
+
+                if (StringUtils.equals(paramBean.getCompare(), "like")) {
+                    value = "%" + value + "%";
+                }
+                sb.append("and ").append(paramBean.getParam()).append(" ").append(paramBean.getCompare());
+                if (value.equals("true") || value.equals("false")) {
+                    sb.append(" ").append(value).append(" ");
+                } else {
+                    sb.append(" '").append(value).append("' ");
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    public static String replaceValue(String value){
+        return value.replaceAll("'","").replaceAll("\"","")
+                .replaceAll(" or ","").replaceAll(" union ","");
+    }
+
+
+    /**
+     * 通用导出
+     */
+    public static void exportData(List datas, List<AdminField> titles, HttpServletRequest request, HttpServletResponse response) throws Exception {
+//        String exportflag = request.getHeader("exportflag");
+//        if(StringUtils.isBlank(exportflag)){
+//            String bodyString = HttpContextUtils.getBodyString(request);
+//            log.info("【通用导出参数】:{}",bodyString);
+//            ExportLogic.add(request,bodyString);
+//            return;
+//        }
+
+        List<List<Object>> rows = new ArrayList<>();
+        List<String> excelTitles = new ArrayList<>();
+        if(CollectionUtils.isEmpty(datas)){
+//            throw new RemoteServiceException("暂无内容导出");
+            excelTitles.add("暂无内容导出");
+        } else {
+//            Map<String, String> jMap = new LinkedHashMap<>();
+            List<String> jList = new ArrayList<>();
+            if(CollectionUtils.isNotEmpty(titles)) {
+//                for (AdminField title : titles) {
+//                    jMap.put(title.getJName(), title.getLabel());
+//                }
+                jList = titles.stream().map(AdminField::getJName).collect(Collectors.toList());
+            }
+            if(jList.isEmpty()){
+                Object bean = datas.get(0);
+                Field[] fields = bean.getClass().getDeclaredFields();
+                for(Field field : fields) {
+                    jList.add(field.getName());
+                }
+            }
+
+            for (int i = 0; i < datas.size(); i++) {
+                Object bean = datas.get(i);
+                List<Object> row = new ArrayList<>();
+                Field[] fields = bean.getClass().getDeclaredFields();
+                List<Field> fieldList = Arrays.asList(fields);
+                Map<String, Field> fieldMap = fieldList.stream().collect(Collectors.toMap(Field::getName, v -> v));
+
+                for(String f:jList){
+                    Field field = fieldMap.get(f);
+                    field.setAccessible(true);
+                    String jName = field.getName();
+                    if (!jList.isEmpty() && !jList.contains(jName)) {
+                        continue;
+                    }
+                    String label = "";
+                    Schema annotation = field.getAnnotation(Schema.class);
+                    if(annotation == null) {
+                        continue;
+                    }
+                    label = annotation.description();
+
+                    ZfireField zfireFieldAnnotation = field.getAnnotation(ZfireField.class);
+                    if(zfireFieldAnnotation != null && zfireFieldAnnotation.hide()) {
+                        continue;
+                    }
+
+                    //属性类型
+                    String typeName = field.getType().getName();
+                    if(typeName.equals("java.util.List") || typeName.equals("java.util.Map")) {
+                        continue;
+                    }
+                    Object value = field.get(bean);
+
+                    //处理枚举类型
+                    if (field.getType().isEnum()) {
+                        for (Object enumO : field.getType().getEnumConstants()) {
+                            Class<?> c = enumO.getClass();
+                            Method getKey = c.getDeclaredMethod("getKey");
+                            Method getValue = c.getDeclaredMethod("getRemark");
+                            Object invoke = getKey.invoke(enumO);
+                            //String enumKey =  invoke+"";
+                            String enumValue = (String) getValue.invoke(enumO);
+                            if (value != null && value.toString().equals(enumO.toString())) {
+                                value = enumValue;
+                            }
+                        }
+                    }
+
+//                    DictConversion dictConversion = field.getAnnotation(DictConversion.class);
+//                    if (dictConversion != null  && value instanceof String && StringUtils.isNotBlank((String) value)) {
+//                        value = DictUtils.getDictDataValue(dictConversion.value(), (String) value);
+//                    }
+
+                    if (value == null) {
+                        row.add("");
+                    }
+
+                    if (value != null) {
+                        if ("java.util.Date".equals(typeName)) {
+                            //todo 格式化时间
+                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                            row.add(sdf.format(value));
+                        } else if (field.getType().isAssignableFrom(LocalDateTime.class)) {
+                            row.add(LocalDateTimeUtil.formatNormal((LocalDateTime) value));
+                        } else if (field.getType().isAssignableFrom(LocalDate.class)) {
+                            row.add(LocalDateTimeUtil.formatNormal((LocalDate) value));
+                        } else {
+                            row.add(value);
+                        }
+                    }
+                    if (i == 0) {
+                        excelTitles.add(label);
+                    }
+                }
+                rows.add(row);
+            }
+        }
+        ExcelData excelData = new ExcelData();
+        excelData.setRows(rows);
+        excelData.setTitles(excelTitles);
+        //目前是异步导出,名字由前端根据菜单名定义
+        //ExcelUtils.exportExcel(request,response,"test.xlsx",excelData);
+        export(excelData,"test.xlsx");
+    }
+
+
+    public static void export(ExcelData excelData,String fileName) throws IOException {
+        OutputStream outputStream = null;
+        try {
+            //标题
+            List<List<String>> titles = new ArrayList<>();
+            for(String title : excelData.getTitles()){
+                titles.add(Arrays.asList(title));
+            }
+
+            //记录总数:实际中需要根据查询条件进行统计即可
+            Integer totalCount = excelData.getRows().size();
+            //每一个Sheet存放100w条数据
+            Integer sheetDataRows = 1000000;
+            //每次写入的数据量20w,每页查询20W
+            Integer writeDataRows = 200000;
+            //计算需要的Sheet数量
+            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
+            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
+            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
+            //计算最后一个sheet需要写入的次数
+            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));
+            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            HttpServletResponse response = requestAttributes.getResponse();
+            outputStream = response.getOutputStream();
+
+            //样式
+            WriteCellStyle cellStyle = writeCellStyle();
+
+            //必须放到循环外,否则会刷新流
+            ExcelWriter excelWriter = EasyExcel.write(outputStream).registerWriteHandler(new CellWriteHandler(){
+                public void afterCellDispose(CellWriteHandlerContext context) {
+                    CellWriteHandler.super.afterCellDispose(context);
+                    if (BooleanUtils.isNotTrue(context.getHead())) {
+                        context.getFirstCellData().setWriteCellStyle(cellStyle);
+                    }
+                }
+            }).build();
+            //开始分批查询分次写入
+            for (int i = 0; i < sheetNum; i++) {
+                //创建Sheet
+//                WriteSheet sheet = new WriteSheet();
+//                sheet.setSheetName("Sheet"+i);
+//                sheet.setSheetNo(i);
+                //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
+                for (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
+                    //分页查询一次20w
+                    WriteSheet writeSheet = EasyExcel.writerSheet(i, "Sheet" + (i + 1)).head(titles)
+                            .registerWriteHandler(new SimpleColumnWidthStyleStrategy(30))
+                            .build();
+                    //写数据
+                    excelWriter.write(excelData.getRows(), writeSheet);
+                }
+            }
+            // 下载EXCEL
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            // 这里URLEncoder.encode可以防止浏览器端导出excel文件名中文乱码 当然和easyexcel没有关系
+            //String fileName = URLEncoder.encode("员工信息", "UTF-8").replaceAll("\\+", "%20");
+            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
+            excelWriter.finish();
+            outputStream.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (BeansException e) {
+            e.printStackTrace();
+        }finally {
+            if (outputStream != null) {
+                outputStream.close();
+            }
+        }
+    }
+
+
+    /**
+     * excel样式
+     * @return
+     */
+    public static WriteCellStyle writeCellStyle(){
+        //样式
+        WriteFont font = new WriteFont();
+        font.setFontName("simsun");
+        font.setFontHeightInPoints((short)10);
+        font.setColor(IndexedColors.BLACK.index);
+        WriteCellStyle cellStyle = new WriteCellStyle();
+        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        cellStyle.setWriteFont(font);
+        cellStyle.setWrapped(false);
+        cellStyle.setBorderTop(BorderStyle.THIN);
+        cellStyle.setBorderLeft(BorderStyle.THIN);
+        cellStyle.setBorderBottom(BorderStyle.THIN);
+        cellStyle.setBorderRight(BorderStyle.THIN);
+        return cellStyle;
+    }
+
+
+}

+ 78 - 0
src/main/resources/application-dev.properties

@@ -0,0 +1,78 @@
+
+domain.url=https://jiasm.zfire.top
+sys.url=${domain.url}${server.servlet.context-path}
+
+#定时器
+schedule.enable=false
+
+##SQL执行分析,该插件有性能损耗,不建议生产环境使用
+spring.datasource.druid.db-type=mysql
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.datasource.url=jdbc:mysql://121.43.111.127:3306/mall_contest?verifyServerCertificate=false&useSSL=false&requireSSL=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=utf8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=xch!eCdvc124@
+
+#prd
+#spring.datasource.url=jdbc:mysql://rm-bp1gsex17k87vu0npmo.mysql.rds.aliyuncs.com:6033/sxb-mall?verifyServerCertificate=false&useSSL=false&requireSSL=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=utf8&serverTimezone=Asia/Shanghai
+#spring.datasource.username=sxb
+#spring.datasource.password=asdf3xFcs*aWx
+
+spring.datasource.druid.connection-init-sqls=set names utf8mb4
+spring.datasource.druid.initial-size=5
+spring.datasource.druid.minIdle=5
+spring.datasource.druid.maxActive=20
+### 配置获取连接等待超时的时间,单位是毫秒
+spring.datasource.druid.maxWait=60000
+### 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
+### 配置一个连接在池中最小生存的时间,单位是毫秒
+spring.datasource.druid.minEvictableIdleTimeMillis=300000
+spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
+spring.datasource.druid.testWhileIdle=true
+spring.datasource.druid.testOnBorrow=false
+spring.datasource.druid.testOnReturn=false
+### 打开PSCache,并且指定每个连接上PSCache的大小
+spring.datasource.druid.poolPreparedStatements=true
+spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
+### 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+spring.datasource.druid.filters=stat,wall
+
+### 通过connectProperties属性来打开mergeSql功能;慢SQL记录
+spring.datasource.druid.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+# 合并多个DruidDataSource的监控数据
+#spring.datasource.useGlobalDataSourceStat=true
+
+
+
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+
+
+#####################redis 单机版 start################
+spring.redis.port=6388
+spring.redis.host=121.43.111.127
+#redis密码
+spring.redis.password=private123@^
+spring.redis.lettuce.pool.max-active=300
+spring.redis.lettuce.pool.max-wait=1000ms
+spring.redis.lettuce.pool.max-idle=100
+spring.redis.lettuce.pool.min-idle=0
+spring.redis.timeout=100000ms
+
+
+####################阿里云配置######################
+ali.access.key.id=LTAI5tDHiEGKuwzNhW2gLPyc
+ali.access.key.secert=NVXSx3Gj7fHv6unGwWgalJh49uMZOI
+ali.oss.bucket.name=taijuhuanxin
+ali.oss.endpoint.ww=oss-cn-shenzhen.aliyuncs.com/
+ali.oss.endpoint=oss-cn-shenzhen-internal.aliyuncs.com/
+ali.oss.type.pic=uploadfile/
+ali.sms.msg.code=SMS_284585310
+ali.cdn.url=
+
+jwt.secret=zfire@2025_base_code_security_key_123456
+jwt.expire=7200
+
+knife4j.enable=true
+knife4j.setting.language=zh_cn

+ 73 - 0
src/main/resources/application-prd.properties

@@ -0,0 +1,73 @@
+
+domain.url=https://taij.gztjhx.com
+sys.url=${domain.url}${server.servlet.context-path}
+
+#定时器
+schedule.enable=true
+
+##SQL执行分析,该插件有性能损耗,不建议生产环境使用
+spring.datasource.druid.db-type=mysql
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/taij?verifyServerCertificate=false&useSSL=false&requireSSL=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=utf8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=root
+spring.datasource.druid.connection-init-sqls=set names utf8mb4
+spring.datasource.druid.initial-size=5
+spring.datasource.druid.minIdle=5
+spring.datasource.druid.maxActive=20
+### 配置获取连接等待超时的时间,单位是毫秒
+spring.datasource.druid.maxWait=60000
+### 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
+### 配置一个连接在池中最小生存的时间,单位是毫秒
+spring.datasource.druid.minEvictableIdleTimeMillis=300000
+spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
+spring.datasource.druid.testWhileIdle=true
+spring.datasource.druid.testOnBorrow=false
+spring.datasource.druid.testOnReturn=false
+### 打开PSCache,并且指定每个连接上PSCache的大小
+spring.datasource.druid.poolPreparedStatements=true
+spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
+### 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+spring.datasource.druid.filters=stat,wall
+
+### 通过connectProperties属性来打开mergeSql功能;慢SQL记录
+spring.datasource.druid.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+# 合并多个DruidDataSource的监控数据
+#spring.datasource.useGlobalDataSourceStat=true
+
+
+
+#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+
+
+#####################redis 单机版 start################
+spring.redis.port=6379
+spring.redis.host=localhost
+#redis密码
+spring.redis.password=
+spring.redis.lettuce.pool.max-active=300
+spring.redis.lettuce.pool.max-wait=1000ms
+spring.redis.lettuce.pool.max-idle=100
+spring.redis.lettuce.pool.min-idle=0
+spring.redis.timeout=100000ms
+
+
+####################阿里云配置######################
+ali.access.key.id=LTAI5t7zNDgo519geeALw74J
+ali.access.key.secert=wV6QoFA72W3CJlhWBDcCwJM4QyqsWq
+ali.oss.bucket.name=taij
+ali.oss.endpoint.ww=oss-cn-shenzhen.aliyuncs.com/
+ali.oss.endpoint=oss-cn-shenzhen-internal.aliyuncs.com/
+ali.oss.type.pic=uploadfile/
+ali.sms.msg.code=SMS_296731679
+ali.cdn.url=
+
+
+jwt.secret=zfire@2025_base_code_security_key_123456
+jwt.expire=7200
+
+knife4j.enable=false
+knife4j.setting.language=zh_cn

+ 66 - 0
src/main/resources/application-test.properties

@@ -0,0 +1,66 @@
+
+domain.url=https://jiasm.zfire.top
+sys.url=${domain.url}${server.servlet.context-path}
+
+#定时器
+schedule.enable=true
+
+##SQL执行分析,该插件有性能损耗,不建议生产环境使用
+spring.datasource.druid.db-type=mysql
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.datasource.url=jdbc:mysql://121.43.111.127:3306/taij?verifyServerCertificate=false&useSSL=false&requireSSL=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=utf8&serverTimezone=Asia/Shanghai
+spring.datasource.username=root
+spring.datasource.password=xch!eCdvc124@
+spring.datasource.druid.connection-init-sqls=set names utf8mb4
+spring.datasource.druid.initial-size=5
+spring.datasource.druid.minIdle=5
+spring.datasource.druid.maxActive=20
+### 配置获取连接等待超时的时间,单位是毫秒
+spring.datasource.druid.maxWait=60000
+### 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
+### 配置一个连接在池中最小生存的时间,单位是毫秒
+spring.datasource.druid.minEvictableIdleTimeMillis=300000
+spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
+spring.datasource.druid.testWhileIdle=true
+spring.datasource.druid.testOnBorrow=false
+spring.datasource.druid.testOnReturn=false
+### 打开PSCache,并且指定每个连接上PSCache的大小
+spring.datasource.druid.poolPreparedStatements=true
+spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
+### 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+spring.datasource.druid.filters=stat,wall
+
+### 通过connectProperties属性来打开mergeSql功能;慢SQL记录
+spring.datasource.druid.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
+# 合并多个DruidDataSource的监控数据
+#spring.datasource.useGlobalDataSourceStat=true
+
+
+#####################redis 单机版 start################
+spring.redis.port=6388
+spring.redis.host=121.43.111.127
+#redis密码
+spring.redis.password=private123@^
+spring.redis.lettuce.pool.max-active=300
+spring.redis.lettuce.pool.max-wait=1000ms
+spring.redis.lettuce.pool.max-idle=100
+spring.redis.lettuce.pool.min-idle=0
+spring.redis.timeout=100000ms
+
+####################阿里云配置######################
+ali.access.key.id=LTAI5tDHiEGKuwzNhW2gLPyc
+ali.access.key.secert=NVXSx3Gj7fHv6unGwWgalJh49uMZOI
+ali.oss.bucket.name=taijuhuanxin
+ali.oss.endpoint.ww=oss-cn-shenzhen.aliyuncs.com/
+ali.oss.endpoint=oss-cn-shenzhen-internal.aliyuncs.com/
+ali.oss.type.pic=uploadfile/
+ali.sms.msg.code=SMS_284585310
+ali.cdn.url=
+
+jwt.secret=zfire@2025_base_code_security_key_123456
+jwt.expire=7200
+
+knife4j.enable=true
+knife4j.setting.language=zh_cn

+ 47 - 0
src/main/resources/application.properties

@@ -0,0 +1,47 @@
+#nacos
+spring.cloud.nacos.config.enabled=false
+spring.cloud.nacos.discovery.enabled=false
+spring.cloud.nacos.config.file-extension=properties
+
+spring.profiles.active=dev
+spring.application.name=mall-contest-service
+spring.main.allow-bean-definition-overriding=true
+
+goods.share.limit.percent=0.4
+#wechat.keyPath=classpath:/static/apiclient_cert.p12
+
+#��������
+server.port =23006
+server.servlet.context-path=/api
+spring.servlet.multipart.max-file-size=40MB
+spring.servlet.multipart.max-request-size=40MB
+#�Ƿ�ֱ��web�˴�ӡ������Ϣ
+spring.devtools.add-properties=true
+#echache����
+spring.cache.type=ehcache
+spring.cache.ehcache.config=classpath:ehcache.xml
+
+mybatis-plus.typeEnumsPackage=com.gree.mall.manager.enums
+
+### log4j����
+logging.config=classpath:logback.xml
+#undertow_web��������
+server.undertow.accesslog.dir=${user.dir}/logs/access
+server.undertow.accesslog.enabled=true
+server.undertow.accesslog.pattern=%h %l %u %t "%r" %s %b %D
+server.undertow.accesslog.prefix=access_log.
+server.undertow.accesslog.rotate=true
+server.undertow.accesslog.suffix=log
+server.undertow.max-http-post-size=10240000
+#swagger-login-account
+#spring.security.user.name=admin
+#spring.security.user.password=gree2021
+#mybatis-plus����
+mybatis-plus.mapper-locations=classpath*:mapper/*.xml,classpath*:mapper/**/*Mapper.xml
+#jackson���ʱ��
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+spring.jackson.timeZone=GMT+08:00
+spring.jackson.serialization.write_dates_as_timestamps=false
+spring.mvc.date-format=yyyy-MM-dd HH:mm:ss
+web.upload-path=${user.dir}/static/

+ 4 - 0
src/main/resources/generator.properties

@@ -0,0 +1,4 @@
+java.package=com.gree.mall.contest
+jdbc.url=jdbc:mysql://121.43.111.127:3306/mall_contest?verifyServerCertificate=false&useSSL=false&requireSSL=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=utf8
+jdbc.username=root
+jdbc.pwd=xch!eCdvc124@

Някои файлове не бяха показани, защото твърде много файлове са промени