新增品牌关系表

This commit is contained in:
Mrking 2024-08-30 08:12:13 +08:00
parent 320e76dad4
commit 1c9bca7345
681 changed files with 15813 additions and 83935 deletions

119
.flattened-pom.xml Normal file
View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>嘉晋OMS系统</description>
<modules>
<module>hangtag-dependencies</module>
<module>hangtag-framework</module>
<module>hangtag-server</module>
<module>hangtag-module-system</module>
<module>hangtag-module-infra</module>
<module>hangtag-module-oms</module>
</modules>
<properties>
<lombok.version>1.18.30</lombok.version>
<java.version>1.8</java.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<maven.compiler.target>${java.version}</maven.compiler.target>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<revision>2.1.0-jdk8-snapshot</revision>
<spring.boot.version>2.7.18</spring.boot.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-dependencies</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>huaweicloud</id>
<name>huawei</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
</repository>
<repository>
<id>aliyunmaven</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
<configuration>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
<updatePomFile>true</updatePomFile>
</configuration>
</plugin>
</plugins>
</build>
</project>

12
bin/clean.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 清理工程target生成路径。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean
pause

12
bin/package.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 打包Web工程生成war/jar包文件。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean package -Dmaven.test.skip=true
pause

14
bin/run.bat Normal file
View File

@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Web工程。
echo.
cd %~dp0
cd ../mikeenk-admin/target
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -jar %JAVA_OPTS% mikeenk-admin.jar
cd bin
pause

View File

@ -0,0 +1,547 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-dependencies</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>基础 bom 文件,管理整个项目的依赖版本</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
<podam.version>7.2.11.RELEASE</podam.version>
<velocity.version>2.3</velocity.version>
<spring.boot.version>2.7.18</spring.boot.version>
<tika-core.version>2.9.1</tika-core.version>
<lock4j.version>2.2.7</lock4j.version>
<okio.version>3.5.0</okio.version>
<guice.version>5.1.0</guice.version>
<lombok.version>1.18.30</lombok.version>
<xercesImpl.version>2.12.2</xercesImpl.version>
<jsoup.version>1.17.2</jsoup.version>
<fastjson.version>1.2.83</fastjson.version>
<commons-net.version>3.10.0</commons-net.version>
<hutool.version>5.8.25</hutool.version>
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
<flowable.version>6.8.0</flowable.version>
<redisson.version>3.18.0</redisson.version>
<easy-trans.version>2.2.11</easy-trans.version>
<okhttp3.version>4.11.0</okhttp3.version>
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<druid.version>1.2.21</druid.version>
<commons-io.version>2.15.1</commons-io.version>
<easyexcel.verion>3.3.3</easyexcel.verion>
<springdoc.version>1.6.15</springdoc.version>
<minio.version>8.5.7</minio.version>
<mockito-inline.version>4.11.0</mockito-inline.version>
<jedis-mock.version>1.0.13</jedis-mock.version>
<tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version>
<dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
<rocketmq-spring.version>2.2.3</rocketmq-spring.version>
<ip2region.version>2.7.0</ip2region.version>
<jsch.version>0.1.55</jsch.version>
<weixin-java.version>4.6.0</weixin-java.version>
<aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>
<captcha-plus.version>1.0.10</captcha-plus.version>
<dynamic-datasource.version>4.3.0</dynamic-datasource.version>
<servlet.versoin>2.5</servlet.versoin>
<knife4j.version>4.3.0</knife4j.version>
<spring-boot-admin.version>2.7.15</spring-boot-admin.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<jimureport.version>1.6.6</jimureport.version>
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
<justauth.version>1.0.8</justauth.version>
<screw.version>1.0.5</screw.version>
<guava.version>33.0.0-jre</guava.version>
<skywalking.version>8.12.0</skywalking.version>
<opentracing.version>0.33.0</opentracing.version>
<mybatis-plus-generator.version>3.5.5</mybatis-plus-generator.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<revision>2.1.0-jdk8-snapshot</revision>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.github.mouzt</groupId>
<artifactId>bizlog-sdk</artifactId>
<version>${bizlog-sdk.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-tenant</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-data-permission</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-ip</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-websocket</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource.version}</version>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>${mybatis-plus-join.version}</version>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-spring-boot-starter</artifactId>
<version>${easy-trans.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-context</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-cloud-commons</artifactId>
<groupId>org.springframework.cloud</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
<version>${easy-trans.version}</version>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-anno</artifactId>
<version>${easy-trans.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-actuator</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm8.jdbc.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-job</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq-spring.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-protection</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
<exclusions>
<exclusion>
<artifactId>redisson-spring-boot-starter</artifactId>
<groupId>org.redisson</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-monitor</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-opentracing</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-noop</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-admin-server-cloud</artifactId>
<groupId>de.codecentric</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito-inline.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<artifactId>mockito-core</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.fppt</groupId>
<artifactId>jedis-mock</artifactId>
<version>${jedis-mock.version}</version>
</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId>
<artifactId>podam</artifactId>
<version>${podam.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-actuator</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-excel</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.verion}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${transmittable-thread-local.version}</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>${jsch.version}</version>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-captcha-plus</artifactId>
<version>${captcha-plus.version}</version>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${ip2region.version}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun-java-sdk-core.version}</version>
<exclusions>
<exclusion>
<artifactId>opentracing-api</artifactId>
<groupId>io.opentracing</groupId>
</exclusion>
<exclusion>
<artifactId>opentracing-util</artifactId>
<groupId>io.opentracing</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>${aliyun-java-sdk-dysmsapi.version}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${tencentcloud-sdk-java.version}</version>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-justauth</artifactId>
<version>${justauth.version}</version>
<exclusions>
<exclusion>
<artifactId>hutool-core</artifactId>
<groupId>cn.hutool</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>${jimureport.version}</version>
<exclusions>
<exclusion>
<artifactId>druid</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>${xercesImpl.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
<configuration>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
<updatePomFile>true</updatePomFile>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<description>该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
1. core 包:是该组件的核心封装
2. config 包:是该组件基于 Spring 的配置
技术组件,也分成两类:
1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展
2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。
如果是业务组件Maven 名字会包含 biz</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<modules>
<module>hangtag-common</module>
<module>hangtag-spring-boot-starter-mybatis</module>
<module>hangtag-spring-boot-starter-redis</module>
<module>hangtag-spring-boot-starter-web</module>
<module>hangtag-spring-boot-starter-security</module>
<module>hangtag-spring-boot-starter-websocket</module>
<module>hangtag-spring-boot-starter-monitor</module>
<module>hangtag-spring-boot-starter-protection</module>
<module>hangtag-spring-boot-starter-job</module>
<module>hangtag-spring-boot-starter-mq</module>
<module>hangtag-spring-boot-starter-excel</module>
<module>hangtag-spring-boot-starter-test</module>
<module>hangtag-spring-boot-starter-biz-tenant</module>
<module>hangtag-spring-boot-starter-biz-data-permission</module>
<module>hangtag-spring-boot-starter-biz-ip</module>
</modules>
</project>

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>定义基础 pojo 类、枚举、工具类等等</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-anno</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-data-permission</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>数据权限</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-ip</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>IP 拓展,支持如下功能:
1. IP 功能:查询 IP 对应的城市信息
基于 https://gitee.com/lionsoul/ip2region 实现
2. 城市功能:查询城市编码对应的城市信息
基于 https://github.com/modood/Administrative-divisions-of-China 实现</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-tenant</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>多租户</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-job</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -11,7 +11,8 @@
"name": "hangtag.tenant.enable",
"type": "java.lang.Boolean",
"description": "是否开启",
"sourceType": "cn.hangtag.framework.tenant.config.TenantProperties"
"sourceType": "cn.hangtag.framework.tenant.config.TenantProperties",
"defaultValue": true
},
{
"name": "hangtag.tenant.ignore-tables",

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-excel</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>Excel 拓展</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-ip</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-job</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>任务拓展
1. 定时任务,基于 Quartz 拓展
2. 异步任务,基于 Spring Async 拓展</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-monitor</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>服务监控,提供链路追踪、日志服务、指标收集等等功能</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-opentracing</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>数据库连接池、多数据源、事务、MyBatis 拓展</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-protection</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>服务保证提供分布式锁、幂等、限流、熔断、API 签名等等功能</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>Redis 封装拓展</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -11,7 +11,8 @@
"name": "hangtag.cache.redis-scan-batch-size",
"type": "java.lang.Integer",
"description": "redis scan 一次返回数量",
"sourceType": "cn.hangtag.framework.redis.config.HangtagCacheProperties"
"sourceType": "cn.hangtag.framework.redis.config.HangtagCacheProperties",
"defaultValue": 30
}
],
"hints": []

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>1. security用户的认证、权限的校验实现「谁」可以做「什么事」
2. operatelog操作日志实现「谁」在「什么时间」对「什么」做了「什么事」</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>io.github.mouzt</groupId>
<artifactId>bizlog-sdk</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>

View File

@ -11,19 +11,22 @@
"name": "hangtag.security.mock-enable",
"type": "java.lang.Boolean",
"description": "mock 模式的开关",
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
"defaultValue": false
},
{
"name": "hangtag.security.mock-secret",
"type": "java.lang.String",
"description": "mock 模式的密钥 一定要配置密钥,保证安全性",
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
"defaultValue": "test"
},
{
"name": "hangtag.security.password-encoder-length",
"type": "java.lang.Integer",
"description": "PasswordEncoder 加密复杂度,越高开销越大",
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
"defaultValue": 4
},
{
"name": "hangtag.security.permit-all-urls",
@ -35,13 +38,15 @@
"name": "hangtag.security.token-header",
"type": "java.lang.String",
"description": "HTTP 请求时,访问令牌的请求 Header",
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
"defaultValue": "Authorization"
},
{
"name": "hangtag.security.token-parameter",
"type": "java.lang.String",
"description": "HTTP 请求时,访问令牌的请求参数 初始目的:解决 WebSocket 无法通过 header 传参,只能通过 token 参数拼接",
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
"defaultValue": "token"
}
],
"hints": []

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>测试组件,用于单元测试、集成测试</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>com.github.fppt</groupId>
<artifactId>jedis-mock</artifactId>
</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId>
<artifactId>podam</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>Web 框架全局异常、API 日志、脱敏、错误码等</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -114,7 +114,8 @@
"name": "hangtag.xss.enable",
"type": "java.lang.Boolean",
"description": "是否开启,默认为 true",
"sourceType": "cn.hangtag.framework.xss.config.XssProperties"
"sourceType": "cn.hangtag.framework.xss.config.XssProperties",
"defaultValue": true
},
{
"name": "hangtag.xss.exclude-urls",

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-framework</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-websocket</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>WebSocket 框架,支持多节点的广播</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-tenant</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -11,13 +11,15 @@
"name": "hangtag.websocket.path",
"type": "java.lang.String",
"description": "WebSocket 的连接路径",
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties"
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties",
"defaultValue": "\/ws"
},
{
"name": "hangtag.websocket.sender-type",
"type": "java.lang.String",
"description": "消息发送器的类型 可选值local、redis、rocketmq、kafka、rabbitmq",
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties"
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties",
"defaultValue": "local"
}
],
"hints": []

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>infra 模块,主要提供两块能力:
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等</description>
<modules>
<module>hangtag-module-infra-api</module>
<module>hangtag-module-infra-biz</module>
</modules>
</project>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-api</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>infra 模块 API暴露给其它模块调用</description>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>infra 模块,主要提供两块能力:
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等</description>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-job</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-monitor</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<modules>
<module>hangtag-module-oms-api</module>
<module>hangtag-module-oms-biz</module>
</modules>
</project>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms-api</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>oms-api 模块 API暴露给其它模块调用</description>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -11,5 +11,6 @@ public interface ErrorCodeConstants extends cn.hangtag.module.system.enums.Erro
ErrorCode DRAFT_DESIGN_DATA_NOT_EXISTS = new ErrorCode(3500, "稿件模板数据 不存在");
ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(3600, "OMS销售订单主表不存在");
ErrorCode SALE_ORDER_ENTRY_NOT_EXISTS = new ErrorCode(3700, "OMS销售订单明细不存在");
ErrorCode CUSTOMER_BRAND_NOT_EXISTS = new ErrorCode(3800, "客户和品牌关联不存在");
}

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms-api</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,45 @@
package cn.hangtag.module.oms.controller.admin.app;
import cn.hangtag.framework.common.enums.CommonStatusEnum;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.security.core.LoginUser;
import cn.hangtag.framework.security.core.util.SecurityFrameworkUtils;
import cn.hangtag.module.oms.controller.admin.brand.vo.BrandListReqVO;
import cn.hangtag.module.oms.controller.admin.brand.vo.BrandSimpleRespVO;
import cn.hangtag.module.oms.dal.dataobject.brand.BrandDO;
import cn.hangtag.module.oms.service.brand.BrandService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "APP - 品牌")
@RestController
@RequestMapping("/oms/app/brand")
@Validated
public class AppBrandController {
@Resource
private BrandService brandService;
@GetMapping({"/list-all-simple", "simple-list"})
@Operation(summary = "获取品牌精简信息列表", description = "只包含被开启的菜单,用于【客户分配品牌】功能的选项。" +
"在多租户的场景下,会只返回租户所在套餐有的菜单")
public CommonResult<List<BrandSimpleRespVO>> getSimpleBrandList() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
List<BrandDO> list = brandService.getBrandList(
new BrandListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
return success(BeanUtils.toBean(list, BrandSimpleRespVO.class));
}
}

View File

@ -0,0 +1,73 @@
package cn.hangtag.module.oms.controller.admin.app;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.ip.core.utils.AreaUtils;
import cn.hangtag.module.oms.controller.admin.customer.vo.CustomerPageReqVO;
import cn.hangtag.module.oms.controller.admin.customer.vo.CustomerRespVO;
import cn.hangtag.module.oms.dal.dataobject.customer.CustomerDO;
import cn.hangtag.module.oms.service.customer.CustomerService;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
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.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
import static java.util.Collections.singletonList;
@Tag(name = "APP - 客户")
@RestController
@RequestMapping("/oms/app/customer")
@Validated
public class AppCustomerController {
@Resource
private CustomerService customerService;
@GetMapping("/page")
public CommonResult<PageResult<CustomerRespVO>> getCustomerPage(@Valid CustomerPageReqVO pageReqVO) {
PageResult<CustomerDO> pageResult = customerService.getCustomerPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得客户")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oms:customer:query')")
public CommonResult<CustomerRespVO> getCustomer(@RequestParam("id") Long id) {
CustomerDO customer = customerService.getCustomer(id);
return success(buildClueDetail(customer));
}
private CustomerRespVO buildClueDetail(CustomerDO clue) {
if (clue == null) {
return null;
}
return buildDetailList(singletonList(clue)).get(0);
}
private List<CustomerRespVO> buildDetailList(List<CustomerDO> list) {
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 2. 转换成 VO
return BeanUtils.toBean(list, CustomerRespVO.class, customerVO -> {
customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId()));
});
}
}

View File

@ -0,0 +1,39 @@
package cn.hangtag.module.oms.controller.admin.app;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoPageReqVO;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoRespVO;
import cn.hangtag.module.oms.dal.dataobject.productinfo.ProductInfoDO;
import cn.hangtag.module.oms.service.productinfo.ProductInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "APP - 物料")
@RestController
@RequestMapping("/oms/app/material")
@Validated
public class AppMaterialController {
@Resource
private ProductInfoService productInfoService;
@GetMapping("/page")
@Operation(summary = "获得产品资料 分页")
@PreAuthorize("@ss.hasPermission('oms:product-info:query')")
public CommonResult<PageResult<ProductInfoRespVO>> getProductInfoPage(@Valid ProductInfoPageReqVO pageReqVO) {
PageResult<ProductInfoDO> pageResult = productInfoService.getProductInfoPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ProductInfoRespVO.class));
}
}

View File

@ -0,0 +1,20 @@
package cn.hangtag.module.oms.controller.admin.app;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "APP - 销售订单")
@RestController
@RequestMapping("/oms/app/saleorder")
@Validated
public class SaleOrderController {
}

View File

@ -1,33 +1,33 @@
package cn.hangtag.module.oms.controller.admin.brand;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
import cn.hangtag.framework.common.enums.CommonStatusEnum;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
import cn.hangtag.framework.excel.core.util.ExcelUtils;
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.hangtag.module.oms.controller.admin.brand.vo.*;
import cn.hangtag.module.oms.dal.dataobject.brand.BrandDO;
import cn.hangtag.module.oms.service.brand.BrandService;
import cn.hangtag.module.system.controller.admin.permission.vo.menu.MenuSimpleRespVO;
import cn.hangtag.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 品牌管理 ")
@RestController
@ -93,4 +93,23 @@ public class BrandController {
BeanUtils.toBean(list, BrandRespVO.class));
}
@GetMapping({"/list-all-simple", "simple-list"})
@Operation(summary = "获取品牌精简信息列表", description = "只包含被开启的菜单,用于【客户分配品牌】功能的选项。" +
"在多租户的场景下,会只返回租户所在套餐有的菜单")
public CommonResult<List<BrandSimpleRespVO>> getSimpleBrandList() {
List<BrandDO> list = brandService.getBrandList(
new BrandListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
return success(BeanUtils.toBean(list, BrandSimpleRespVO.class));
}
@Operation(summary = "获得客户拥有的品牌编号")
@Parameter(name = "customerId", description = "角色编号", required = true)
@GetMapping("/list-customer-brand")
//@PreAuthorize("@ss.hasPermission('oms:brand:assign-customer-brand')")
public CommonResult<Set<Long>> getRoleMenuList(Long customerId) {
return success(brandService.getCustomerBrandListByCustomerId(customerId));
}
}

View File

@ -0,0 +1,16 @@
package cn.hangtag.module.oms.controller.admin.brand.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 菜单列表 Request VO")
@Data
public class BrandListReqVO {
@Schema(description = "菜单名称,模糊匹配", example = "芋道")
private String name;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
private Integer status;
}

View File

@ -0,0 +1,19 @@
package cn.hangtag.module.oms.controller.admin.brand.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 菜单精简信息 Response VO")
@Data
public class BrandSimpleRespVO {
@Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
}

View File

@ -21,6 +21,9 @@ public class CustomerPageReqVO extends PageParam {
@Schema(description = "名称", example = "赵六")
private String name;
@Schema(description = "公司", example = "赵六")
private String company;
@Schema(description = "类型", example = "2")
private String type;

View File

@ -22,6 +22,10 @@ public class CustomerRespVO {
@ExcelProperty("名称")
private String name;
@Schema(description = "公司")
@ExcelProperty("公司")
private String company;
@Schema(description = "邮箱")
@ExcelProperty("邮箱")
private String email;

View File

@ -18,6 +18,9 @@ public class CustomerSaveReqVO {
@Schema(description = "名称", example = "赵六")
private String name;
@Schema(description = "公司")
private String company;
@Schema(description = "邮箱")
private String email;

View File

@ -0,0 +1,102 @@
package cn.hangtag.module.oms.controller.admin.customerbrand;
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.excel.core.util.ExcelUtils;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.CustomerBrandPageReqVO;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.CustomerBrandRespVO;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.CustomerBrandSaveReqVO;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.PermissionAssignCustomerBrandReqVO;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.module.oms.service.customerbrand.CustomerBrandService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 客户和品牌关联")
@RestController
@RequestMapping("/oms/customer-brand")
@Validated
public class CustomerBrandController {
@Resource
private CustomerBrandService customerBrandService;
@PostMapping("/create")
@Operation(summary = "创建客户和品牌关联")
@PreAuthorize("@ss.hasPermission('oms:customer-brand:create')")
public CommonResult<Long> createCustomerBrand(@Valid @RequestBody CustomerBrandSaveReqVO createReqVO) {
return success(customerBrandService.createCustomerBrand(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新客户和品牌关联")
@PreAuthorize("@ss.hasPermission('oms:customer-brand:update')")
public CommonResult<Boolean> updateCustomerBrand(@Valid @RequestBody CustomerBrandSaveReqVO updateReqVO) {
customerBrandService.updateCustomerBrand(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客户和品牌关联")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('oms:customer-brand:delete')")
public CommonResult<Boolean> deleteCustomerBrand(@RequestParam("id") Long id) {
customerBrandService.deleteCustomerBrand(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得客户和品牌关联")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oms:customer-brand:query')")
public CommonResult<CustomerBrandRespVO> getCustomerBrand(@RequestParam("id") Long id) {
CustomerBrandDO customerBrand = customerBrandService.getCustomerBrand(id);
return success(BeanUtils.toBean(customerBrand, CustomerBrandRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得客户和品牌关联分页")
@PreAuthorize("@ss.hasPermission('oms:customer-brand:query')")
public CommonResult<PageResult<CustomerBrandRespVO>> getCustomerBrandPage(@Valid CustomerBrandPageReqVO pageReqVO) {
PageResult<CustomerBrandDO> pageResult = customerBrandService.getCustomerBrandPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CustomerBrandRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出客户和品牌关联 Excel")
@PreAuthorize("@ss.hasPermission('oms:customer-brand:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportCustomerBrandExcel(@Valid CustomerBrandPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CustomerBrandDO> list = customerBrandService.getCustomerBrandPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "客户和品牌关联.xls", "数据", CustomerBrandRespVO.class,
BeanUtils.toBean(list, CustomerBrandRespVO.class));
}
@Operation(summary = "赋予客户品牌")
@PostMapping("/assign-customer-brand")
//@PreAuthorize("@ss.hasPermission('oms:customer:assign-customer-brabnd')")
public CommonResult<Boolean> assignUserRole(@Validated @RequestBody PermissionAssignCustomerBrandReqVO reqVO) {
customerBrandService.assignCustomerBrand(reqVO.getCustomerId(), reqVO.getBrandIds());
return success(true);
}
}

View File

@ -0,0 +1,31 @@
package cn.hangtag.module.oms.controller.admin.customerbrand.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.hangtag.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.hangtag.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客户和品牌关联分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CustomerBrandPageReqVO extends PageParam {
@Schema(description = "自增编号", example = "6786")
private Long id;
@Schema(description = "客户ID", example = "15433")
private Long customerId;
@Schema(description = "品牌ID", example = "4361")
private Long brandId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,31 @@
package cn.hangtag.module.oms.controller.admin.customerbrand.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 客户和品牌关联 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CustomerBrandRespVO {
@Schema(description = "自增编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6786")
@ExcelProperty("自增编号")
private Long id;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "15433")
@ExcelProperty("客户ID")
private Long customerId;
@Schema(description = "品牌ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4361")
@ExcelProperty("品牌ID")
private Long brandId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,25 @@
package cn.hangtag.module.oms.controller.admin.customerbrand.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 客户和品牌关联新增/修改 Request VO")
@Data
public class CustomerBrandSaveReqVO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21486")
@ExcelProperty("ID")
private Long id;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "15433")
@NotNull(message = "客户ID不能为空")
private Long customerId;
@Schema(description = "品牌ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4361")
@NotNull(message = "品牌ID不能为空")
private Long brandId;
}

View File

@ -0,0 +1,21 @@
package cn.hangtag.module.oms.controller.admin.customerbrand.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Set;
@Schema(description = "管理后台 - 赋予客户品牌 Request VO")
@Data
public class PermissionAssignCustomerBrandReqVO {
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "客户编号不能为空")
private Long customerId;
@Schema(description = "品牌编号列表", example = "1,3,5")
private Set<Long> brandIds = Collections.emptySet(); // 兜底
}

View File

@ -0,0 +1,106 @@
package cn.hangtag.module.oms.controller.admin.saleorder;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.common.util.object.BeanUtils;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
import cn.hangtag.framework.excel.core.util.ExcelUtils;
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
import cn.hangtag.module.oms.service.saleorder.SaleOrderService;
@Tag(name = "管理后台 - 销售订单")
@RestController
@RequestMapping("/oms/sale-order")
@Validated
public class SaleOrderController {
@Resource
private SaleOrderService saleOrderService;
@PostMapping("/create")
@Operation(summary = "创建销售订单")
@PreAuthorize("@ss.hasPermission('oms:sale-order:create')")
public CommonResult<Long> createSaleOrder(@Valid @RequestBody SaleOrderSaveReqVO createReqVO) {
return success(saleOrderService.createSaleOrder(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新销售订单")
@PreAuthorize("@ss.hasPermission('oms:sale-order:update')")
public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody SaleOrderSaveReqVO updateReqVO) {
saleOrderService.updateSaleOrder(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除销售订单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('oms:sale-order:delete')")
public CommonResult<Boolean> deleteSaleOrder(@RequestParam("id") Long id) {
saleOrderService.deleteSaleOrder(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得销售订单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oms:sale-order:query')")
public CommonResult<SaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
SaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
return success(BeanUtils.toBean(saleOrder, SaleOrderRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得销售订单分页")
@PreAuthorize("@ss.hasPermission('oms:sale-order:query')")
public CommonResult<PageResult<SaleOrderRespVO>> getSaleOrderPage(@Valid SaleOrderPageReqVO pageReqVO) {
PageResult<SaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, SaleOrderRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出销售订单 Excel")
@PreAuthorize("@ss.hasPermission('oms:sale-order:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportSaleOrderExcel(@Valid SaleOrderPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<SaleOrderDO> list = saleOrderService.getSaleOrderPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "销售订单.xls", "数据", SaleOrderRespVO.class,
BeanUtils.toBean(list, SaleOrderRespVO.class));
}
// ==================== 子表销售订单明细 ====================
@GetMapping("/sale-order-entry/list-by-parent-id")
@Operation(summary = "获得销售订单明细列表")
@Parameter(name = "parentId", description = "主表id")
@PreAuthorize("@ss.hasPermission('oms:sale-order:query')")
public CommonResult<List<SaleOrderEntryDO>> getSaleOrderEntryListByParentId(@RequestParam("parentId") Long parentId) {
return success(saleOrderService.getSaleOrderEntryListByParentId(parentId));
}
}

View File

@ -0,0 +1,42 @@
package cn.hangtag.module.oms.controller.admin.saleorder.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.hangtag.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.hangtag.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 销售订单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SaleOrderPageReqVO extends PageParam {
@Schema(description = "单据编号")
private String billno;
@Schema(description = "客户id", example = "10257")
private Long customerId;
@Schema(description = "业务日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] bizdate;
@Schema(description = "确认日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] confirmdate;
@Schema(description = "计划日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] plansenddate;
@Schema(description = "手机")
private String phone;
@Schema(description = "备注")
private String remarks;
}

View File

@ -0,0 +1,55 @@
package cn.hangtag.module.oms.controller.admin.saleorder.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 销售订单 Response VO")
@Data
@ExcelIgnoreUnannotated
public class SaleOrderRespVO {
@Schema(description = "单据编号")
@ExcelProperty("单据编号")
private String billno;
@Schema(description = "客户id", example = "10257")
@ExcelProperty("客户id")
private Long customerId;
@Schema(description = "业务日期")
@ExcelProperty("业务日期")
private LocalDateTime bizdate;
@Schema(description = "备注", example = "你猜")
@ExcelProperty("备注")
private String remark;
@Schema(description = "更新时间")
@ExcelProperty("更新时间")
private LocalDateTime udate;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime cdate;
@Schema(description = "确认日期")
@ExcelProperty("确认日期")
private LocalDateTime confirmdate;
@Schema(description = "计划日期")
@ExcelProperty("计划日期")
private LocalDateTime plansenddate;
@Schema(description = "手机")
@ExcelProperty("手机")
private String phone;
@Schema(description = "备注")
@ExcelProperty("备注")
private String remarks;
}

View File

@ -0,0 +1,62 @@
package cn.hangtag.module.oms.controller.admin.saleorder.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
@Schema(description = "管理后台 - 销售订单新增/修改 Request VO")
@Data
public class SaleOrderSaveReqVO {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21486")
@ExcelProperty("ID")
private Long id;
@Schema(description = "客户id", example = "10257")
private Long customerId;
@Schema(description = "业务日期")
private LocalDateTime bizdate;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "确认日期")
private LocalDateTime confirmdate;
@Schema(description = "计划日期")
private LocalDateTime plansenddate;
@Schema(description = "手机")
private String phone;
@Schema(description = "备注")
private String remarks;
@Schema(description = "邮件列表数据格式xxx@xx.com;xxx@xx.com;")
private String emails;
@Schema(description = "发票抬头")
private String invoiceCode;
@Schema(description = "发票名称", example = "李四")
private String invoiceName;
@Schema(description = "地址")
private String address;
@Schema(description = "货币")
private String currency;
@Schema(description = "发票备注")
private String invoiceRemarks;
@Schema(description = "销售订单明细列表")
private List<SaleOrderEntryDO> saleOrderEntrys;
}

View File

@ -35,6 +35,10 @@ public class CustomerDO extends BaseDO {
* 名称
*/
private String name;
/**
* 公司
*/
private String company;
/**
* 邮箱
*/

View File

@ -0,0 +1,39 @@
package cn.hangtag.module.oms.dal.dataobject.customerbrand;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
/**
* 客户和品牌关联 DO
*
* @author wwb
*/
@TableName("oms_customer_brand")
@KeySequence("oms_customer_brand_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerBrandDO extends BaseDO {
/**
* 自增编号
*/
@TableId
private Long id;
/**
* 客户ID
*/
private Long customerId;
/**
* 品牌ID
*/
private Long brandId;
}

View File

@ -0,0 +1,106 @@
package cn.hangtag.module.oms.dal.dataobject.saleorder;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
/**
* 销售订单 DO
*
* @author wwb
*/
@TableName("oms_saleorder")
@KeySequence("oms_saleorder_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SaleOrderDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
* 单据编号
*/
private String billno;
/**
* 客户id
*/
private Long customerId;
/**
* 业务日期
*/
private LocalDateTime bizdate;
/**
* 备注
*/
private String remark;
/**
* 更新时间
*/
private LocalDateTime udate;
/**
* 创建时间
*/
private LocalDateTime cdate;
/**
* 确认日期
*/
private LocalDateTime confirmdate;
/**
* 计划日期
*/
private LocalDateTime plansenddate;
/**
* 订单类型
*/
private String orderType;
/**
* 手机
*/
private String phone;
/**
* 传真
*/
private String fax;
/**
* 备注
*/
private String remarks;
/**
* 邮件列表数据格式xxx@xx.com;xxx@xx.com;
*/
private String emails;
/**
* 发票抬头
*/
private String invoiceCode;
/**
* 发票名称
*/
private String invoiceName;
/**
* 地址
*/
private String address;
/**
* 货币
*/
private String currency;
/**
* 发票备注
*/
private String invoiceRemarks;
}

View File

@ -0,0 +1,54 @@
package cn.hangtag.module.oms.dal.dataobject.saleorderentry;
import lombok.*;
import java.util.*;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
/**
* 销售订单明细 DO
*
* @author 芋道源码
*/
@TableName("oms_saleorder_entry")
@KeySequence("oms_saleorder_entry_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SaleOrderEntryDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
* 主表id
*/
private Long parentId;
/**
* 物料id
*/
private Integer materialId;
/**
* 物料名称
*/
private String materialName;
/**
* 物料规格
*/
private String materialSpec;
/**
* 单价
*/
private BigDecimal price;
/**
* 数量
*/
private Integer qty;
}

View File

@ -6,6 +6,7 @@ import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
import cn.hangtag.module.oms.dal.dataobject.brand.BrandDO;
import cn.hangtag.module.system.dal.dataobject.permission.MenuDO;
import org.apache.ibatis.annotations.Mapper;
import cn.hangtag.module.oms.controller.admin.brand.vo.*;
@ -28,4 +29,8 @@ public interface BrandMapper extends BaseMapperX<BrandDO> {
.orderByDesc(BrandDO::getId));
}
default List<BrandDO> selectList(BrandListReqVO reqVO){
return selectList(new LambdaQueryWrapperX<BrandDO>()
.likeIfPresent(BrandDO::getName, reqVO.getName()));
}
}

View File

@ -21,6 +21,7 @@ public interface CustomerMapper extends BaseMapperX<CustomerDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerDO>()
.eqIfPresent(CustomerDO::getId, reqVO.getId())
.likeIfPresent(CustomerDO::getName, reqVO.getName())
.likeIfPresent(CustomerDO::getCompany, reqVO.getCompany())
.eqIfPresent(CustomerDO::getType, reqVO.getType())
.eqIfPresent(CustomerDO::getStatus, reqVO.getStatus())
.orderByDesc(CustomerDO::getId));

View File

@ -0,0 +1,43 @@
package cn.hangtag.module.oms.dal.mysql.customerbrand;
import java.util.*;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.hangtag.module.system.dal.dataobject.permission.UserRoleDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.*;
/**
* 客户和品牌关联 Mapper
*
* @author wwb
*/
@Mapper
public interface CustomerBrandMapper extends BaseMapperX<CustomerBrandDO> {
default PageResult<CustomerBrandDO> selectPage(CustomerBrandPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerBrandDO>()
.eqIfPresent(CustomerBrandDO::getId, reqVO.getId())
.eqIfPresent(CustomerBrandDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(CustomerBrandDO::getBrandId, reqVO.getBrandId())
.betweenIfPresent(CustomerBrandDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CustomerBrandDO::getId));
}
default List<CustomerBrandDO> selectListByCustomerId(Long customerId) {
return selectList(CustomerBrandDO::getCustomerId, customerId);
}
default List<CustomerBrandDO> selectListByCustomerId(Collection<Long> customerIds) {
return selectList(CustomerBrandDO::getCustomerId, customerIds);
}
default void deleteListByCustomerIdAndBrandIdIds(Long customerId, Collection<Long> deleteBrandIds) {
delete(new LambdaQueryWrapper<CustomerBrandDO>()
.eq(CustomerBrandDO::getCustomerId, customerId)
.in(CustomerBrandDO::getBrandId, deleteBrandIds));
}
}

View File

@ -0,0 +1,28 @@
package cn.hangtag.module.oms.dal.mysql.saleorderentry;
import java.util.*;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 销售订单明细 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface SaleOrderEntryMapper extends BaseMapperX<SaleOrderEntryDO> {
default List<SaleOrderEntryDO> selectListByParentId(Long parentId) {
return selectList(SaleOrderEntryDO::getParentId, parentId);
}
default int deleteByParentId(Long parentId) {
return delete(SaleOrderEntryDO::getParentId, parentId);
}
}

View File

@ -0,0 +1,32 @@
package cn.hangtag.module.oms.dal.mysql.saleorder;
import java.util.*;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import org.apache.ibatis.annotations.Mapper;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
/**
* 销售订单 Mapper
*
* @author wwb
*/
@Mapper
public interface SaleOrderMapper extends BaseMapperX<SaleOrderDO> {
default PageResult<SaleOrderDO> selectPage(SaleOrderPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SaleOrderDO>()
.eqIfPresent(SaleOrderDO::getBillno, reqVO.getBillno())
.eqIfPresent(SaleOrderDO::getCustomerId, reqVO.getCustomerId())
.betweenIfPresent(SaleOrderDO::getBizdate, reqVO.getBizdate())
.betweenIfPresent(SaleOrderDO::getConfirmdate, reqVO.getConfirmdate())
.betweenIfPresent(SaleOrderDO::getPlansenddate, reqVO.getPlansenddate())
.eqIfPresent(SaleOrderDO::getPhone, reqVO.getPhone())
.eqIfPresent(SaleOrderDO::getRemarks, reqVO.getRemarks())
.orderByDesc(SaleOrderDO::getId));
}
}

View File

@ -7,6 +7,8 @@ import cn.hangtag.module.oms.dal.dataobject.brand.BrandDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
import static java.util.Collections.singleton;
/**
* 品牌管理 Service 接口
*
@ -54,4 +56,17 @@ public interface BrandService {
String getNewCode();
List<BrandDO> getBrandList(BrandListReqVO reqVO);
default Set<Long> getCustomerBrandListByCustomerId(Long customerId){
return getCustomerBrandListByCustomerId(singleton(customerId));
}
/**
* 获得客户们拥有的品牌编号集合
*
* @param customerIds 客户编号数组
* @return 品牌编号集合
*/
Set<Long> getCustomerBrandListByCustomerId(Collection<Long> customerIds);
}

View File

@ -5,8 +5,14 @@ import cn.hangtag.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.hangtag.framework.common.util.FuncUtil;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
import cn.hangtag.module.oms.base.dal.dataobject.producttype.ProductTypeDO;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.module.oms.dal.mysql.customerbrand.CustomerBrandMapper;
import cn.hangtag.module.oms.enums.BrandErrorCodeConstants;
import cn.hangtag.module.oms.serialnumber.CodingRulesUtils;
import cn.hangtag.module.system.dal.dataobject.permission.MenuDO;
import cn.hangtag.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.hangtag.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
@ -26,6 +32,8 @@ import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.module.oms.dal.mysql.brand.BrandMapper;
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.hangtag.framework.common.util.collection.CollectionUtils.convertSet;
import static java.util.Collections.singleton;
/**
* 品牌管理 Service 实现类
@ -39,6 +47,8 @@ public class BrandServiceImpl implements BrandService {
@Resource
private BrandMapper brandMapper;
@Resource
private CustomerBrandMapper customerBrandMapper;
@Override
public Long createBrand(BrandSaveReqVO createReqVO) {
@ -114,6 +124,22 @@ public class BrandServiceImpl implements BrandService {
}
}
}
@Override
public List<BrandDO> getBrandList(BrandListReqVO reqVO) {
List<BrandDO> brands = brandMapper.selectList(reqVO);
return brands;
}
@Override
public Set<Long> getCustomerBrandListByCustomerId(Collection<Long> customerIds) {
if (CollUtil.isEmpty(customerIds)) {
return Collections.emptySet();
}
return convertSet(customerBrandMapper.selectListByCustomerId(customerIds), CustomerBrandDO::getBrandId);
}
private void checkCode(Long id,String code){
if(FuncUtil.isNotEmpty(code)){
LambdaQueryWrapper<BrandDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();

View File

@ -53,7 +53,7 @@ public class CustomerServiceImpl implements CustomerService {
userSaveReqVO.setNickname(createReqVO.getName());
userSaveReqVO.setMobile(createReqVO.getPhone());
userSaveReqVO.setPassword(userInitPassword);
userSaveReqVO.setDeptId(999L); // 固定为用户分类
userSaveReqVO.setDeptId(999999L); // 固定为用户分类
Long userId = userService.createUser(userSaveReqVO);
customer.setUserId(userId);
customerMapper.insert(customer);

View File

@ -0,0 +1,56 @@
package cn.hangtag.module.oms.service.customerbrand;
import java.util.*;
import javax.validation.*;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.*;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
/**
* 客户和品牌关联 Service 接口
*
* @author wwb
*/
public interface CustomerBrandService {
/**
* 创建客户和品牌关联
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCustomerBrand(@Valid CustomerBrandSaveReqVO createReqVO);
/**
* 更新客户和品牌关联
*
* @param updateReqVO 更新信息
*/
void updateCustomerBrand(@Valid CustomerBrandSaveReqVO updateReqVO);
/**
* 删除客户和品牌关联
*
* @param id 编号
*/
void deleteCustomerBrand(Long id);
/**
* 获得客户和品牌关联
*
* @param id 编号
* @return 客户和品牌关联
*/
CustomerBrandDO getCustomerBrand(Long id);
/**
* 获得客户和品牌关联分页
*
* @param pageReqVO 分页查询
* @return 客户和品牌关联分页
*/
PageResult<CustomerBrandDO> getCustomerBrandPage(CustomerBrandPageReqVO pageReqVO);
void assignCustomerBrand(Long customerId, Set<Long> brandIds);
}

View File

@ -0,0 +1,107 @@
package cn.hangtag.module.oms.service.customerbrand;
import cn.hangtag.framework.common.util.collection.CollectionUtils;
import cn.hangtag.module.system.dal.dataobject.permission.UserRoleDO;
import cn.hangtag.module.system.dal.redis.RedisKeyConstants;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.*;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.module.oms.dal.mysql.customerbrand.CustomerBrandMapper;
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.hangtag.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
/**
* 客户和品牌关联 Service 实现类
*
* @author wwb
*/
@Service
@Validated
public class CustomerBrandServiceImpl implements CustomerBrandService {
@Resource
private CustomerBrandMapper customerBrandMapper;
@Override
public Long createCustomerBrand(CustomerBrandSaveReqVO createReqVO) {
// 插入
CustomerBrandDO customerBrand = BeanUtils.toBean(createReqVO, CustomerBrandDO.class);
customerBrandMapper.insert(customerBrand);
// 返回
return customerBrand.getId();
}
@Override
public void updateCustomerBrand(CustomerBrandSaveReqVO updateReqVO) {
// 校验存在
validateCustomerBrandExists(updateReqVO.getId());
// 更新
CustomerBrandDO updateObj = BeanUtils.toBean(updateReqVO, CustomerBrandDO.class);
customerBrandMapper.updateById(updateObj);
}
@Override
public void deleteCustomerBrand(Long id) {
// 校验存在
validateCustomerBrandExists(id);
// 删除
customerBrandMapper.deleteById(id);
}
private void validateCustomerBrandExists(Long id) {
if (customerBrandMapper.selectById(id) == null) {
throw exception(CUSTOMER_BRAND_NOT_EXISTS);
}
}
@Override
public CustomerBrandDO getCustomerBrand(Long id) {
return customerBrandMapper.selectById(id);
}
@Override
public PageResult<CustomerBrandDO> getCustomerBrandPage(CustomerBrandPageReqVO pageReqVO) {
return customerBrandMapper.selectPage(pageReqVO);
}
@Override
@DSTransactional // 多数据源使用 @DSTransactional 保证本地事务以及数据源的切换
@CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#customerId")
public void assignCustomerBrand(Long customerId, Set<Long> brandIds) {
// 获得客户拥有品牌编号
Set<Long> dbBrandIds = convertSet(customerBrandMapper.selectListByCustomerId(customerId),
CustomerBrandDO::getBrandId);
// 计算新增和删除的角色编号
Set<Long> brandIdList = CollUtil.emptyIfNull(brandIds);
Collection<Long> createRoleIds = CollUtil.subtract(brandIdList, dbBrandIds);
Collection<Long> deleteMenuIds = CollUtil.subtract(dbBrandIds, brandIdList);
// 执行新增和删除对于已经授权的角色不用做任何处理
if (!CollectionUtil.isEmpty(createRoleIds)) {
customerBrandMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
CustomerBrandDO entity = new CustomerBrandDO();
entity.setCustomerId(customerId);
entity.setBrandId(roleId);
return entity;
}));
}
if (!CollectionUtil.isEmpty(deleteMenuIds)) {
customerBrandMapper.deleteListByCustomerIdAndBrandIdIds(customerId, deleteMenuIds);
}
}
}

View File

@ -0,0 +1,66 @@
package cn.hangtag.module.oms.service.saleorder;
import java.util.*;
import javax.validation.*;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
/**
* 销售订单 Service 接口
*
* @author wwb
*/
public interface SaleOrderService {
/**
* 创建销售订单
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createSaleOrder(@Valid SaleOrderSaveReqVO createReqVO);
/**
* 更新销售订单
*
* @param updateReqVO 更新信息
*/
void updateSaleOrder(@Valid SaleOrderSaveReqVO updateReqVO);
/**
* 删除销售订单
*
* @param id 编号
*/
void deleteSaleOrder(Long id);
/**
* 获得销售订单
*
* @param id 编号
* @return 销售订单
*/
SaleOrderDO getSaleOrder(Long id);
/**
* 获得销售订单分页
*
* @param pageReqVO 分页查询
* @return 销售订单分页
*/
PageResult<SaleOrderDO> getSaleOrderPage(SaleOrderPageReqVO pageReqVO);
// ==================== 子表销售订单明细 ====================
/**
* 获得销售订单明细列表
*
* @param parentId 主表id
* @return 销售订单明细列表
*/
List<SaleOrderEntryDO> getSaleOrderEntryListByParentId(Long parentId);
}

View File

@ -0,0 +1,112 @@
package cn.hangtag.module.oms.service.saleorder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.module.oms.dal.mysql.saleorder.SaleOrderMapper;
import cn.hangtag.module.oms.dal.mysql.saleorderentry.SaleOrderEntryMapper;
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
/**
* 销售订单 Service 实现类
*
* @author wwb
*/
@Service
@Validated
public class SaleOrderServiceImpl implements SaleOrderService {
@Resource
private SaleOrderMapper saleOrderMapper;
@Resource
private SaleOrderEntryMapper saleOrderEntryMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createSaleOrder(SaleOrderSaveReqVO createReqVO) {
// 插入
SaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, SaleOrderDO.class);
saleOrderMapper.insert(saleOrder);
// 插入子表
createSaleOrderEntryList(saleOrder.getId(), createReqVO.getSaleOrderEntrys());
// 返回
return saleOrder.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateSaleOrder(SaleOrderSaveReqVO updateReqVO) {
// 校验存在
validateSaleOrderExists(updateReqVO.getId());
// 更新
SaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, SaleOrderDO.class);
saleOrderMapper.updateById(updateObj);
// 更新子表
updateSaleOrderEntryList(updateReqVO.getId(), updateReqVO.getSaleOrderEntrys());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteSaleOrder(Long id) {
// 校验存在
validateSaleOrderExists(id);
// 删除
saleOrderMapper.deleteById(id);
// 删除子表
deleteSaleOrderEntryByParentId(id);
}
private void validateSaleOrderExists(Long id) {
if (saleOrderMapper.selectById(id) == null) {
throw exception(SALE_ORDER_NOT_EXISTS);
}
}
@Override
public SaleOrderDO getSaleOrder(Long id) {
return saleOrderMapper.selectById(id);
}
@Override
public PageResult<SaleOrderDO> getSaleOrderPage(SaleOrderPageReqVO pageReqVO) {
return saleOrderMapper.selectPage(pageReqVO);
}
// ==================== 子表销售订单明细 ====================
@Override
public List<SaleOrderEntryDO> getSaleOrderEntryListByParentId(Long parentId) {
return saleOrderEntryMapper.selectListByParentId(parentId);
}
private void createSaleOrderEntryList(Long parentId, List<SaleOrderEntryDO> list) {
list.forEach(o -> o.setParentId(parentId));
saleOrderEntryMapper.insertBatch(list);
}
private void updateSaleOrderEntryList(Long parentId, List<SaleOrderEntryDO> list) {
deleteSaleOrderEntryByParentId(parentId);
list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下1id 冲突2updateTime 不更新
createSaleOrderEntryList(parentId, list);
}
private void deleteSaleOrderEntryByParentId(Long parentId) {
saleOrderEntryMapper.deleteByParentId(parentId);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.hangtag.module.oms.dal.mysql.customerbrand.CustomerBrandMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.hangtag.module.oms.dal.mysql.saleorder.SaleOrderMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,142 @@
package cn.hangtag.module.oms.service.customerbrand;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.hangtag.framework.test.core.ut.BaseDbUnitTest;
import cn.hangtag.module.oms.controller.admin.customerbrand.vo.*;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
import cn.hangtag.module.oms.dal.mysql.customerbrand.CustomerBrandMapper;
import cn.hangtag.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
import static cn.hangtag.framework.test.core.util.AssertUtils.*;
import static cn.hangtag.framework.test.core.util.RandomUtils.*;
import static cn.hangtag.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.hangtag.framework.common.util.object.ObjectUtils.*;
import static cn.hangtag.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link CustomerBrandServiceImpl} 的单元测试类
*
* @author wwb
*/
@Import(CustomerBrandServiceImpl.class)
public class CustomerBrandServiceImplTest extends BaseDbUnitTest {
@Resource
private CustomerBrandServiceImpl customerBrandService;
@Resource
private CustomerBrandMapper customerBrandMapper;
@Test
public void testCreateCustomerBrand_success() {
// 准备参数
CustomerBrandSaveReqVO createReqVO = randomPojo(CustomerBrandSaveReqVO.class).setId(null);
// 调用
Long customerBrandId = customerBrandService.createCustomerBrand(createReqVO);
// 断言
assertNotNull(customerBrandId);
// 校验记录的属性是否正确
CustomerBrandDO customerBrand = customerBrandMapper.selectById(customerBrandId);
assertPojoEquals(createReqVO, customerBrand, "id");
}
@Test
public void testUpdateCustomerBrand_success() {
// mock 数据
CustomerBrandDO dbCustomerBrand = randomPojo(CustomerBrandDO.class);
customerBrandMapper.insert(dbCustomerBrand);// @Sql: 先插入出一条存在的数据
// 准备参数
CustomerBrandSaveReqVO updateReqVO = randomPojo(CustomerBrandSaveReqVO.class, o -> {
o.setId(dbCustomerBrand.getId()); // 设置更新的 ID
});
// 调用
customerBrandService.updateCustomerBrand(updateReqVO);
// 校验是否更新正确
CustomerBrandDO customerBrand = customerBrandMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, customerBrand);
}
@Test
public void testUpdateCustomerBrand_notExists() {
// 准备参数
CustomerBrandSaveReqVO updateReqVO = randomPojo(CustomerBrandSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> customerBrandService.updateCustomerBrand(updateReqVO), CUSTOMER_BRAND_NOT_EXISTS);
}
@Test
public void testDeleteCustomerBrand_success() {
// mock 数据
CustomerBrandDO dbCustomerBrand = randomPojo(CustomerBrandDO.class);
customerBrandMapper.insert(dbCustomerBrand);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbCustomerBrand.getId();
// 调用
customerBrandService.deleteCustomerBrand(id);
// 校验数据不存在了
assertNull(customerBrandMapper.selectById(id));
}
@Test
public void testDeleteCustomerBrand_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> customerBrandService.deleteCustomerBrand(id), CUSTOMER_BRAND_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值然后删除 @Disabled 注解
public void testGetCustomerBrandPage() {
// mock 数据
CustomerBrandDO dbCustomerBrand = randomPojo(CustomerBrandDO.class, o -> { // 等会查询到
o.setId(null);
o.setCustomerId(null);
o.setBrandId(null);
o.setCreateTime(null);
});
customerBrandMapper.insert(dbCustomerBrand);
// 测试 id 不匹配
customerBrandMapper.insert(cloneIgnoreId(dbCustomerBrand, o -> o.setId(null)));
// 测试 customerId 不匹配
customerBrandMapper.insert(cloneIgnoreId(dbCustomerBrand, o -> o.setCustomerId(null)));
// 测试 brandId 不匹配
customerBrandMapper.insert(cloneIgnoreId(dbCustomerBrand, o -> o.setBrandId(null)));
// 测试 createTime 不匹配
customerBrandMapper.insert(cloneIgnoreId(dbCustomerBrand, o -> o.setCreateTime(null)));
// 准备参数
CustomerBrandPageReqVO reqVO = new CustomerBrandPageReqVO();
reqVO.setId(null);
reqVO.setCustomerId(null);
reqVO.setBrandId(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<CustomerBrandDO> pageResult = customerBrandService.getCustomerBrandPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbCustomerBrand, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,154 @@
package cn.hangtag.module.oms.service.saleorder;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import javax.annotation.Resource;
import cn.hangtag.framework.test.core.ut.BaseDbUnitTest;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.dal.mysql.saleorder.SaleOrderMapper;
import cn.hangtag.framework.common.pojo.PageResult;
import javax.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
import static cn.hangtag.framework.test.core.util.AssertUtils.*;
import static cn.hangtag.framework.test.core.util.RandomUtils.*;
import static cn.hangtag.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.hangtag.framework.common.util.object.ObjectUtils.*;
import static cn.hangtag.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link SaleOrderServiceImpl} 的单元测试类
*
* @author wwb
*/
@Import(SaleOrderServiceImpl.class)
public class SaleOrderServiceImplTest extends BaseDbUnitTest {
@Resource
private SaleOrderServiceImpl saleOrderService;
@Resource
private SaleOrderMapper saleOrderMapper;
@Test
public void testCreateSaleOrder_success() {
// 准备参数
SaleOrderSaveReqVO createReqVO = randomPojo(SaleOrderSaveReqVO.class).setId(null);
// 调用
Long saleOrderId = saleOrderService.createSaleOrder(createReqVO);
// 断言
assertNotNull(saleOrderId);
// 校验记录的属性是否正确
SaleOrderDO saleOrder = saleOrderMapper.selectById(saleOrderId);
assertPojoEquals(createReqVO, saleOrder, "id");
}
@Test
public void testUpdateSaleOrder_success() {
// mock 数据
SaleOrderDO dbSaleOrder = randomPojo(SaleOrderDO.class);
saleOrderMapper.insert(dbSaleOrder);// @Sql: 先插入出一条存在的数据
// 准备参数
SaleOrderSaveReqVO updateReqVO = randomPojo(SaleOrderSaveReqVO.class, o -> {
o.setId(dbSaleOrder.getId()); // 设置更新的 ID
});
// 调用
saleOrderService.updateSaleOrder(updateReqVO);
// 校验是否更新正确
SaleOrderDO saleOrder = saleOrderMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, saleOrder);
}
@Test
public void testUpdateSaleOrder_notExists() {
// 准备参数
SaleOrderSaveReqVO updateReqVO = randomPojo(SaleOrderSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> saleOrderService.updateSaleOrder(updateReqVO), SALE_ORDER_NOT_EXISTS);
}
@Test
public void testDeleteSaleOrder_success() {
// mock 数据
SaleOrderDO dbSaleOrder = randomPojo(SaleOrderDO.class);
saleOrderMapper.insert(dbSaleOrder);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSaleOrder.getId();
// 调用
saleOrderService.deleteSaleOrder(id);
// 校验数据不存在了
assertNull(saleOrderMapper.selectById(id));
}
@Test
public void testDeleteSaleOrder_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> saleOrderService.deleteSaleOrder(id), SALE_ORDER_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值然后删除 @Disabled 注解
public void testGetSaleOrderPage() {
// mock 数据
SaleOrderDO dbSaleOrder = randomPojo(SaleOrderDO.class, o -> { // 等会查询到
o.setBillno(null);
o.setCustomerId(null);
o.setBizdate(null);
o.setConfirmdate(null);
o.setPlansenddate(null);
o.setPhone(null);
o.setRemarks(null);
});
saleOrderMapper.insert(dbSaleOrder);
// 测试 billno 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setBillno(null)));
// 测试 customerId 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setCustomerId(null)));
// 测试 bizdate 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setBizdate(null)));
// 测试 confirmdate 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setConfirmdate(null)));
// 测试 plansenddate 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setPlansenddate(null)));
// 测试 phone 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setPhone(null)));
// 测试 remarks 不匹配
saleOrderMapper.insert(cloneIgnoreId(dbSaleOrder, o -> o.setRemarks(null)));
// 准备参数
SaleOrderPageReqVO reqVO = new SaleOrderPageReqVO();
reqVO.setBillno(null);
reqVO.setCustomerId(null);
reqVO.setBizdate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setConfirmdate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setPlansenddate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setPhone(null);
reqVO.setRemarks(null);
// 调用
PageResult<SaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSaleOrder, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等</description>
<modules>
<module>hangtag-module-system-api</module>
<module>hangtag-module-system-biz</module>
</modules>
</project>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>system 模块 API暴露给其它模块调用</description>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等</description>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-data-permission</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-biz-ip</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-job</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-mq</artifactId>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-justauth</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-captcha-plus</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -32,6 +32,8 @@ public class AuthLoginReqVO {
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
// ========== 平台账号类型 ============
private Long type;
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,

View File

@ -13,8 +13,12 @@ import java.util.List;
@Mapper
public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
default AdminUserDO selectByUsername(String username) {
return selectOne(AdminUserDO::getUsername, username);
default AdminUserDO selectByUsername(String username,Long type) {
if(type!=null){
return selectOne(AdminUserDO::getUsername, username,AdminUserDO::getDeptId,type);
}else {
return selectOne(AdminUserDO::getUsername, username);
}
}
default AdminUserDO selectByEmail(String email) {

View File

@ -21,7 +21,7 @@ public interface AdminAuthService {
* @param password 密码
* @return 用户
*/
AdminUserDO authenticate(String username, String password);
AdminUserDO authenticate(String username, String password, Long type);
/**
* 账号登录

View File

@ -72,10 +72,10 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private Boolean captchaEnable;
@Override
public AdminUserDO authenticate(String username, String password) {
public AdminUserDO authenticate(String username, String password, Long type) {
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
// 校验账号是否存在
AdminUserDO user = userService.getUserByUsername(username);
AdminUserDO user = userService.getUserByUsername(username,type);
if (user == null) {
createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
@ -98,7 +98,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
validateCaptcha(reqVO);
// 使用账号密码进行登录
AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword(),reqVO.getType());
// 如果 socialType 非空说明需要绑定社交用户
if (reqVO.getSocialType() != null) {

View File

@ -72,7 +72,7 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService {
@Override
public OAuth2AccessTokenDO grantPassword(String username, String password, String clientId, List<String> scopes) {
// 使用账号 + 密码进行登录
AdminUserDO user = adminAuthService.authenticate(username, password);
AdminUserDO user = adminAuthService.authenticate(username, password,null);
Assert.notNull(user, "用户不能为空!"); // 防御性编程
// 创建访问令牌

View File

@ -95,7 +95,7 @@ public interface AdminUserService {
* @param username 用户名
* @return 用户对象信息
*/
AdminUserDO getUserByUsername(String username);
AdminUserDO getUserByUsername(String username,Long type);
/**
* 通过手机号获取用户

View File

@ -237,8 +237,8 @@ public class AdminUserServiceImpl implements AdminUserService {
}
@Override
public AdminUserDO getUserByUsername(String username) {
return userMapper.selectByUsername(username);
public AdminUserDO getUserByUsername(String username,Long type) {
return userMapper.selectByUsername(username,type);
}
@Override
@ -360,7 +360,7 @@ public class AdminUserServiceImpl implements AdminUserService {
if (StrUtil.isBlank(username)) {
return;
}
AdminUserDO user = userMapper.selectByUsername(username);
AdminUserDO user = userMapper.selectByUsername(username,null);
if (user == null) {
return;
}
@ -443,7 +443,7 @@ public class AdminUserServiceImpl implements AdminUserService {
return;
}
// 判断如果不存在在进行插入
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername(),null);
if (existUser == null) {
userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class)
.setPassword(encodePassword(userInitPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组

View File

@ -1,372 +0,0 @@
package cn.hangtag.module.system.service.auth;
import cn.hutool.core.util.ReflectUtil;
import cn.hangtag.framework.common.enums.CommonStatusEnum;
import cn.hangtag.framework.common.enums.UserTypeEnum;
import cn.hangtag.framework.test.core.ut.BaseDbUnitTest;
import cn.hangtag.module.system.api.sms.SmsCodeApi;
import cn.hangtag.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.hangtag.module.system.api.social.dto.SocialUserRespDTO;
import cn.hangtag.module.system.controller.admin.auth.vo.*;
import cn.hangtag.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.hangtag.module.system.dal.dataobject.user.AdminUserDO;
import cn.hangtag.module.system.enums.logger.LoginLogTypeEnum;
import cn.hangtag.module.system.enums.logger.LoginResultEnum;
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
import cn.hangtag.module.system.enums.social.SocialTypeEnum;
import cn.hangtag.module.system.service.logger.LoginLogService;
import cn.hangtag.module.system.service.member.MemberService;
import cn.hangtag.module.system.service.oauth2.OAuth2TokenService;
import cn.hangtag.module.system.service.social.SocialUserService;
import cn.hangtag.module.system.service.user.AdminUserService;
import com.xingyuv.captcha.model.common.ResponseModel;
import com.xingyuv.captcha.service.CaptchaService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hangtag.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.hangtag.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.hangtag.framework.test.core.util.RandomUtils.randomPojo;
import static cn.hangtag.framework.test.core.util.RandomUtils.randomString;
import static cn.hangtag.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(AdminAuthServiceImpl.class)
public class AdminAuthServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminAuthServiceImpl authService;
@MockBean
private AdminUserService userService;
@MockBean
private CaptchaService captchaService;
@MockBean
private LoginLogService loginLogService;
@MockBean
private SocialUserService socialUserService;
@MockBean
private SmsCodeApi smsCodeApi;
@MockBean
private OAuth2TokenService oauth2TokenService;
@MockBean
private MemberService memberService;
@MockBean
private Validator validator;
@BeforeEach
public void setUp() {
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// 注入一个 Validator 对象
ReflectUtil.setFieldValue(authService, "validator",
Validation.buildDefaultValidatorFactory().getValidator());
}
@Test
public void testAuthenticate_success() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
// 调用
AdminUserDO loginUser = authService.authenticate(username, password);
// 校验
assertPojoEquals(user, loginUser);
}
@Test
public void testAuthenticate_userNotFound() {
// 准备参数
String username = randomString();
String password = randomString();
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
&& o.getUserId() == null)
);
}
@Test
public void testAuthenticate_badCredentials() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testAuthenticate_userDisabled() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_USER_DISABLED);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testLogin_success() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
o.setUsername("test_username").setPassword("test_password")
.setSocialType(randomEle(SocialTypeEnum.values()).getType()));
// mock 验证码正确
ReflectUtil.setFieldValue(authService, "captchaEnable", false);
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
.setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用并校验
AuthLoginRespVO loginRespVO = authService.login(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO(
user.getId(), UserTypeEnum.ADMIN.getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())));
}
@Test
public void testSendSmsCode() {
// 准备参数
String mobile = randomString();
Integer scene = randomEle(SmsSceneEnum.values()).getScene();
AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene);
// mock 方法用户信息
AdminUserDO user = randomPojo(AdminUserDO.class);
when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
// 调用
authService.sendSmsCode(reqVO);
// 断言
verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> {
assertEquals(mobile, sendReqDTO.getMobile());
assertEquals(scene, sendReqDTO.getScene());
return true;
}));
}
@Test
public void testSmsLogin_success() {
// 准备参数
String mobile = randomString();
String code = randomString();
AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code);
// mock 方法用户信息
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L));
when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用并断言
AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 断言调用
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testSocialLogin_success() {
// 准备参数
AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
// mock 方法绑定的用户编号
Long userId = 1L;
when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
// mock用户
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用并断言
AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 断言调用
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testValidateCaptcha_successWithEnable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.success());
// 调用无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testValidateCaptcha_successWithDisable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码关闭
ReflectUtil.setFieldValue(authService, "captchaEnable", false);
// 调用无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testValidateCaptcha_constraintViolationException() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// 调用并断言异常
assertThrows(ConstraintViolationException.class, () -> authService.validateCaptcha(reqVO),
"验证码不能为空");
}
@Test
public void testCaptcha_fail() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码打开
ReflectUtil.setFieldValue(authService, "captchaEnable", true);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.errorMsg("就是不对"));
// 调用, 并断言异常
assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对");
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testRefreshToken() {
// 准备参数
String refreshToken = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default")))
.thenReturn(accessTokenDO);
// 调用
AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken);
// 断言
assertPojoEquals(accessTokenDO, loginRespVO);
}
@Test
public void testLogout_success() {
// 准备参数
String token = randomString();
// mock
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
// 调用
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
);
// 调用并校验
}
@Test
public void testLogout_fail() {
// 准备参数
String token = randomString();
// 调用
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
// 校验调用参数
verify(loginLogService, never()).createLoginLog(any());
}
}

View File

@ -1,173 +0,0 @@
package cn.hangtag.module.system.service.oauth2;
import cn.hangtag.framework.common.enums.UserTypeEnum;
import cn.hangtag.framework.test.core.ut.BaseMockitoUnitTest;
import cn.hangtag.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.hangtag.module.system.dal.dataobject.oauth2.OAuth2CodeDO;
import cn.hangtag.module.system.dal.dataobject.user.AdminUserDO;
import cn.hangtag.module.system.service.auth.AdminAuthService;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hangtag.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.hangtag.framework.test.core.util.RandomUtils.*;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link OAuth2GrantServiceImpl} 的单元测试
*
* @author 芋道源码
*/
public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private OAuth2GrantServiceImpl oauth2GrantService;
@Mock
private OAuth2TokenService oauth2TokenService;
@Mock
private OAuth2CodeService oauth2CodeService;
@Mock
private AdminAuthService adminAuthService;
@Test
public void testGrantImplicit() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(userId), eq(userType),
eq(clientId), eq(scopes))).thenReturn(accessTokenDO);
// 调用并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantImplicit(
userId, userType, clientId, scopes));
}
@Test
public void testGrantAuthorizationCodeForCode() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
String redirectUri = randomString();
String state = randomString();
// mock 方法
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class);
when(oauth2CodeService.createAuthorizationCode(eq(userId), eq(userType),
eq(clientId), eq(scopes), eq(redirectUri), eq(state))).thenReturn(codeDO);
// 调用并断言
assertEquals(codeDO.getCode(), oauth2GrantService.grantAuthorizationCodeForCode(userId, userType,
clientId, scopes, redirectUri, state));
}
@Test
public void testGrantAuthorizationCodeForAccessToken() {
// 准备参数
String clientId = randomString();
String code = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
String redirectUri = randomString();
String state = randomString();
// mock 方法code
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class, o -> {
o.setClientId(clientId);
o.setRedirectUri(redirectUri);
o.setState(state);
o.setScopes(scopes);
});
when(oauth2CodeService.consumeAuthorizationCode(eq(code))).thenReturn(codeDO);
// mock 方法创建令牌
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(codeDO.getUserId()), eq(codeDO.getUserType()),
eq(codeDO.getClientId()), eq(codeDO.getScopes()))).thenReturn(accessTokenDO);
// 调用并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantAuthorizationCodeForAccessToken(
clientId, code, redirectUri, state));
}
@Test
public void testGrantPassword() {
// 准备参数
String username = randomString();
String password = randomString();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
// mock 方法(认证)
AdminUserDO user = randomPojo(AdminUserDO.class);
when(adminAuthService.authenticate(eq(username), eq(password))).thenReturn(user);
// mock 方法访问令牌
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()),
eq(clientId), eq(scopes))).thenReturn(accessTokenDO);
// 调用并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantPassword(
username, password, clientId, scopes));
}
@Test
public void testGrantRefreshToken() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq(clientId)))
.thenReturn(accessTokenDO);
// 调用并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantRefreshToken(
refreshToken, clientId));
}
@Test
public void testGrantClientCredentials() {
assertThrows(UnsupportedOperationException.class,
() -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()),
"暂时不支持 client_credentials 授权模式");
}
@Test
public void testRevokeToken_clientIdError() {
// 准备参数
String clientId = randomString();
String accessToken = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// 调用并断言
assertFalse(oauth2GrantService.revokeToken(clientId, accessToken));
}
@Test
public void testRevokeToken_success() {
// 准备参数
String clientId = randomString();
String accessToken = randomString();
// mock 方法访问令牌
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setClientId(clientId);
when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// mock 方法移除
when(oauth2TokenService.removeAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// 调用并断言
assertTrue(oauth2GrantService.revokeToken(clientId, accessToken));
}
}

View File

@ -1,762 +0,0 @@
package cn.hangtag.module.system.service.user;
import cn.hutool.core.util.RandomUtil;
import cn.hangtag.framework.common.enums.CommonStatusEnum;
import cn.hangtag.framework.common.exception.ServiceException;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.collection.ArrayUtils;
import cn.hangtag.framework.common.util.collection.CollectionUtils;
import cn.hangtag.framework.test.core.ut.BaseDbUnitTest;
import cn.hangtag.module.infra.api.file.FileApi;
import cn.hangtag.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import cn.hangtag.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import cn.hangtag.module.system.controller.admin.user.vo.user.*;
import cn.hangtag.module.system.dal.dataobject.dept.DeptDO;
import cn.hangtag.module.system.dal.dataobject.dept.PostDO;
import cn.hangtag.module.system.dal.dataobject.dept.UserPostDO;
import cn.hangtag.module.system.dal.dataobject.tenant.TenantDO;
import cn.hangtag.module.system.dal.dataobject.user.AdminUserDO;
import cn.hangtag.module.system.dal.mysql.dept.UserPostMapper;
import cn.hangtag.module.system.dal.mysql.user.AdminUserMapper;
import cn.hangtag.module.system.enums.common.SexEnum;
import cn.hangtag.module.system.service.dept.DeptService;
import cn.hangtag.module.system.service.dept.PostService;
import cn.hangtag.module.system.service.permission.PermissionService;
import cn.hangtag.module.system.service.tenant.TenantService;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBytes;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hangtag.framework.common.util.collection.SetUtils.asSet;
import static cn.hangtag.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.hangtag.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.hangtag.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.hangtag.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.hangtag.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.hangtag.framework.test.core.util.RandomUtils.*;
import static cn.hangtag.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@Import(AdminUserServiceImpl.class)
public class AdminUserServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminUserServiceImpl userService;
@Resource
private AdminUserMapper userMapper;
@Resource
private UserPostMapper userPostMapper;
@MockBean
private DeptService deptService;
@MockBean
private PostService postService;
@MockBean
private PermissionService permissionService;
@MockBean
private PasswordEncoder passwordEncoder;
@MockBean
private TenantService tenantService;
@MockBean
private FileApi fileApi;
@Test
public void testCreatUser_success() {
// 准备参数
UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> {
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
o.setPostIds(asSet(1L, 2L));
}).setId(null); // 避免 id 被赋值
// mock 账户额度充足
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(1));
doNothing().when(tenantService).handleTenantInfo(argThat(handler -> {
handler.handle(tenant);
return true;
}));
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("hangtagyuanma");
// 调用
Long userId = userService.createUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password", "id");
assertEquals("hangtagyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(1L, userPosts.get(0).getPostId());
assertEquals(2L, userPosts.get(1).getPostId());
}
@Test
public void testCreatUser_max() {
// 准备参数
UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class);
// mock 账户额度不足
TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(-1));
doNothing().when(tenantService).handleTenantInfo(argThat(handler -> {
handler.handle(tenant);
return true;
}));
// 调用并断言异常
assertServiceException(() -> userService.createUser(reqVO), USER_COUNT_MAX, -1);
}
@Test
public void testUpdateUser_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L)));
userMapper.insert(dbUser);
userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L));
userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L));
// 准备参数
UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> {
o.setId(dbUser.getId());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
o.setPostIds(asSet(2L, 3L));
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// 调用
userService.updateUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user, "password");
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(2L, userPosts.get(0).getPostId());
assertEquals(3L, userPosts.get(1).getPostId());
}
@Test
public void testUpdateUserLogin() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setLoginDate(null));
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String loginIp = randomString();
// 调用
userService.updateUserLogin(id, loginIp);
// 断言
AdminUserDO dbUser = userMapper.selectById(id);
assertEquals(loginIp, dbUser.getLoginIp());
assertNotNull(dbUser.getLoginDate());
}
@Test
public void testUpdateUserProfile_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> {
o.setMobile(randomString());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
});
// 调用
userService.updateUserProfile(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserPassword_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou"));
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> {
o.setOldPassword("tudou");
o.setNewPassword("yuanma");
});
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true);
// 调用
userService.updateUserPassword(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:yuanma", user.getPassword());
}
@Test
public void testUpdateUserAvatar_success() throws Exception {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
byte[] avatarFileBytes = randomBytes(10);
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
// mock 方法
String avatar = randomString();
when(fileApi.createFile(eq( avatarFileBytes))).thenReturn(avatar);
// 调用
userService.updateUserAvatar(userId, avatarFile);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(avatar, user.getAvatar());
}
@Test
public void testUpdateUserPassword02_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
String password = "hangtag";
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
// 调用
userService.updateUserPassword(userId, password);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:" + password, user.getPassword());
}
@Test
public void testUpdateUserStatus() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
Integer status = randomCommonStatus();
// 调用
userService.updateUserStatus(userId, status);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(status, user.getStatus());
}
@Test
public void testDeleteUser_success(){
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用数据
userService.deleteUser(userId);
// 校验结果
assertNull(userMapper.selectById(userId));
// 校验调用次数
verify(permissionService, times(1)).processUserDeleted(eq(userId));
}
@Test
public void testGetUserByUsername() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
String username = dbUser.getUsername();
// 调用
AdminUserDO user = userService.getUserByUsername(username);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserByMobile() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
String mobile = dbUser.getMobile();
// 调用
AdminUserDO user = userService.getUserByMobile(mobile);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserPage() {
// mock 数据
AdminUserDO dbUser = initGetUserPageData();
// 准备参数
UserPageReqVO reqVO = new UserPageReqVO();
reqVO.setUsername("tu");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 2L 的父部门
// mock 方法
List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList);
// 调用
PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbUser, pageResult.getList().get(0));
}
/**
* 初始化 getUserPage 方法的测试数据
*/
private AdminUserDO initGetUserPageData() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到
o.setUsername("tudou");
o.setMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
o.setDeptId(2L);
});
userMapper.insert(dbUser);
// 测试 username 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setUsername("dou")));
// 测试 mobile 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setMobile("18818260888")));
// 测试 status 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 测试 dept 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L)));
return dbUser;
}
@Test
public void testGetUser() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用
AdminUserDO user = userService.getUser(userId);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserListByDeptIds() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L));
userMapper.insert(dbUser);
// 测试 deptId 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L)));
// 准备参数
Collection<Long> deptIds = singleton(1L);
// 调用
List<AdminUserDO> list = userService.getUserListByDeptIds(deptIds);
// 断言
assertEquals(1, list.size());
assertEquals(dbUser, list.get(0));
}
/**
* 情况一校验不通过导致插入失败
*/
@Test
public void testImportUserList_01() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
});
// mock 方法模拟失败
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况二不存在进行插入
*/
@Test
public void testImportUserList_02() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq("hangtagyuanma"))).thenReturn("java");
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(1, respVO.getCreateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals("java", user.getPassword());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(0, respVO.getFailureUsernames().size());
}
/**
* 情况三存在但是不强制更新
*/
@Test
public void testImportUserList_03() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), false);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况四存在强制更新
*/
@Test
public void testImportUserList_04() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(1, respVO.getUpdateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals(0, respVO.getFailureUsernames().size());
}
@Test
public void testValidateUserExists_notExists() {
assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS);
}
@Test
public void testValidateUsernameUnique_usernameExistsForCreate() {
// 准备参数
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用校验异常
assertServiceException(() -> userService.validateUsernameUnique(null, username),
USER_USERNAME_EXISTS);
}
@Test
public void testValidateUsernameUnique_usernameExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用校验异常
assertServiceException(() -> userService.validateUsernameUnique(id, username),
USER_USERNAME_EXISTS);
}
@Test
public void testValidateEmailUnique_emailExistsForCreate() {
// 准备参数
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用校验异常
assertServiceException(() -> userService.validateEmailUnique(null, email),
USER_EMAIL_EXISTS);
}
@Test
public void testValidateEmailUnique_emailExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用校验异常
assertServiceException(() -> userService.validateEmailUnique(id, email),
USER_EMAIL_EXISTS);
}
@Test
public void testValidateMobileUnique_mobileExistsForCreate() {
// 准备参数
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用校验异常
assertServiceException(() -> userService.validateMobileUnique(null, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testValidateMobileUnique_mobileExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用校验异常
assertServiceException(() -> userService.validateMobileUnique(id, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testValidateOldPassword_notExists() {
assertServiceException(() -> userService.validateOldPassword(randomLongId(), randomString()),
USER_NOT_EXISTS);
}
@Test
public void testValidateOldPassword_passwordFailed() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String oldPassword = user.getPassword();
// 调用校验异常
assertServiceException(() -> userService.validateOldPassword(id, oldPassword),
USER_PASSWORD_FAILED);
// 校验调用
verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword()));
}
@Test
public void testUserListByPostIds() {
// 准备参数
Collection<Long> postIds = asSet(10L, 20L);
// mock user1 数据
AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L)));
userMapper.insert(user1);
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L));
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L));
// mock user2 数据
AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L)));
userMapper.insert(user2);
userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L));
// 调用
List<AdminUserDO> result = userService.getUserListByPostIds(postIds);
// 断言
assertEquals(1, result.size());
assertEquals(user1, result.get(0));
}
@Test
public void testGetUserList() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 测试 id 不匹配
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
// 调用
List<AdminUserDO> result = userService.getUserList(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testGetUserMap() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 测试 id 不匹配
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
// 调用
Map<Long, AdminUserDO> result = userService.getUserMap(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(user.getId()));
}
@Test
public void testGetUserListByNickname() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setNickname("芋头"));
userMapper.insert(user);
// 测试 nickname 不匹配
userMapper.insert(randomAdminUserDO(o -> o.setNickname("源码")));
// 准备参数
String nickname = "";
// 调用
List<AdminUserDO> result = userService.getUserListByNickname(nickname);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testGetUserListByStatus() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
userMapper.insert(user);
// 测试 status 不匹配
userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
// 准备参数
Integer status = CommonStatusEnum.DISABLE.getStatus();
// 调用
List<AdminUserDO> result = userService.getUserListByStatus(status);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testValidateUserList_success() {
// mock 数据
AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
userMapper.insert(userDO);
// 准备参数
List<Long> ids = singletonList(userDO.getId());
// 调用无需断言
userService.validateUserList(ids);
}
@Test
public void testValidateUserList_notFound() {
// 准备参数
List<Long> ids = singletonList(randomLongId());
// 调用, 并断言异常
assertServiceException(() -> userService.validateUserList(ids), USER_NOT_EXISTS);
}
@Test
public void testValidateUserList_notEnable() {
// mock 数据
AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.DISABLE.getStatus());
userMapper.insert(userDO);
// 准备参数
List<Long> ids = singletonList(userDO.getId());
// 调用, 并断言异常
assertServiceException(() -> userService.validateUserList(ids), USER_IS_DISABLE,
userDO.getNickname());
}
// ========== 随机对象 ==========
@SafeVarargs
private static AdminUserDO randomAdminUserDO(Consumer<AdminUserDO>... consumers) {
Consumer<AdminUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
};
return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -4,7 +4,7 @@ import javax.annotation.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-08-10T18:18:46+0800",
date = "2024-08-27T23:26:02+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
)
public class OAuth2OpenConvertImpl implements OAuth2OpenConvert {

View File

@ -6,7 +6,7 @@ import javax.annotation.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-08-10T18:18:46+0800",
date = "2024-08-27T23:26:02+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
)
public class SocialUserConvertImpl implements SocialUserConvert {

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag</artifactId>
<version>2.1.0-jdk8-snapshot</version>
</parent>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-server</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<name>${project.artifactId}</name>
<description>后端 Server 的主项目,通过引入需要 hangtag-module-xxx 的依赖,
从而实现提供 RESTful API 给 hangtag-ui-admin、hangtag-ui-user 等前端项目。
本质上来说,它就是个空壳(容器)!</description>
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-system-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-infra-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-module-oms-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hangtag</groupId>
<artifactId>hangtag-spring-boot-starter-protection</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<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>
</plugins>
</build>
</project>

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,56 @@
import request from '@/config/axios'
// 品牌管理 VO
export interface BrandVO {
id: number // id
code: string // 系统编码
name: string // 名称
logo: string // logo
brandField: string // 品牌领域 字典brand_industry_field
website: string // 官网
intro: string // 品牌介绍 富文本内容
locale: string // 语言标识 字典-language_locale
remark: string // 备注
}
// 品牌管理 API
export const BrandApi = {
// 查询品牌管理 分页
getBrandPage: async (params: any) => {
return await request.get({ url: `/oms/brand/page`, params })
},
// 查询品牌管理 详情
getBrand: async (id: number) => {
return await request.get({ url: `/oms/brand/get?id=` + id })
},
// 新增品牌管理
createBrand: async (data: BrandVO) => {
return await request.post({ url: `/oms/brand/create`, data })
},
// 修改品牌管理
updateBrand: async (data: BrandVO) => {
return await request.put({ url: `/oms/brand/update`, data })
},
// 删除品牌管理
deleteBrand: async (id: number) => {
return await request.delete({ url: `/oms/brand/delete?id=` + id })
},
// 导出品牌管理 Excel
exportBrand: async (params) => {
return await request.download({ url: `/oms/brand/export-excel`, params })
},
}
import request from '@/config/axios'
import {getSimpleMenusList} from "@/api/system/menu";
// 品牌管理 VO
export interface BrandVO {
id: number // id
code: string // 系统编码
name: string // 名称
logo: string // logo
brandField: string // 品牌领域 字典brand_industry_field
website: string // 官网
intro: string // 品牌介绍 富文本内容
locale: string // 语言标识 字典-language_locale
remark: string // 备注
}
// 品牌管理 API
export const BrandApi = {
// 查询品牌管理 分页
getBrandPage: async (params: any) => {
return await request.get({ url: `/oms/brand/page`, params })
},
// 查询品牌管理 详情
getBrand: async (id: number) => {
return await request.get({ url: `/oms/brand/get?id=` + id })
},
// 新增品牌管理
createBrand: async (data: BrandVO) => {
return await request.post({ url: `/oms/brand/create`, data })
},
// 修改品牌管理
updateBrand: async (data: BrandVO) => {
return await request.put({ url: `/oms/brand/update`, data })
},
// 删除品牌管理
deleteBrand: async (id: number) => {
return await request.delete({ url: `/oms/brand/delete?id=` + id })
},
// 导出品牌管理 Excel
exportBrand: async (params) => {
return await request.download({ url: `/oms/brand/export-excel`, params })
},
}
// 查询品牌(精简)列表
export const getSimpleBrandList = async () => {
return await request.get({ url: `/oms/brand/simple-list`})
}

View File

@ -3,6 +3,7 @@ import request from '@/config/axios'
// 客户 VO
export interface CustomerVO {
name: string // 名称
company: string // 公司
email: string // 邮箱
contacts: string // 联系人
phone: string // 联系人手机号

View File

@ -0,0 +1,59 @@
import request from '@/config/axios'
// 客户和品牌关联 VO
export interface CustomerBrandVO {
customerId: number // 客户ID
brandId: number // 品牌ID
}
export interface PermissionAssignCustomerBrandReqVO {
customerId: number
brandIds: number[]
}
// 客户和品牌关联 API
export const CustomerBrandApi = {
// 查询客户和品牌关联分页
getCustomerBrandPage: async (params: any) => {
return await request.get({ url: `/oms/customer-brand/page`, params })
},
// 查询客户和品牌关联详情
getCustomerBrand: async (id: number) => {
return await request.get({ url: `/oms/customer-brand/get?id=` + id })
},
// 新增客户和品牌关联
createCustomerBrand: async (data: CustomerBrandVO) => {
return await request.post({ url: `/oms/customer-brand/create`, data })
},
// 修改客户和品牌关联
updateCustomerBrand: async (data: CustomerBrandVO) => {
return await request.put({ url: `/oms/customer-brand/update`, data })
},
// 删除客户和品牌关联
deleteCustomerBrand: async (id: number) => {
return await request.delete({ url: `/oms/customer-brand/delete?id=` + id })
},
// 导出客户和品牌关联 Excel
exportCustomerBrand: async (params) => {
return await request.download({ url: `/oms/customer-brand/export-excel`, params })
},
}
// 查询客户拥有的品牌权限
export const getCustomerBrandList = async (customerId: number) => {
return await request.get({ url: '/oms/brand/list-customer-brand?customerId=' + customerId })
}
// 赋予角色菜单权限
export const assignCustomerBrand = async (data: PermissionAssignCustomerBrandReqVO) => {
return await request.post({ url: '/oms/customer-brand/assign-customer-brand', data })
}

View File

@ -1,19 +1,13 @@
import request from '@/config/axios'
// OMS销售订单主表 VO
// 销售订单 VO
export interface SaleOrderVO {
id: number // ID
billno: string // 单据编号
customerId: number // 客户id
bizdate: Date // 业务日期
remark: string // 备注
udate: Date // 更新时间
cdate: Date // 创建时间
confirmdate: Date // 确认日期
plansenddate: Date // 计划日期
orderType: string // 订单类型
phone: string // 手机
fax: string // 传真
remarks: string // 备注
emails: string // 邮件列表数据格式xxx@xx.com;xxx@xx.com;
invoiceCode: string // 发票抬头
@ -23,61 +17,42 @@ export interface SaleOrderVO {
invoiceRemarks: string // 发票备注
}
// OMS销售订单主表 API
// 销售订单 API
export const SaleOrderApi = {
// 查询OMS销售订单主表分页
// 查询销售订单分页
getSaleOrderPage: async (params: any) => {
return await request.get({ url: `/oms/sale-order/page`, params })
},
// 查询OMS销售订单主表详情
// 查询销售订单详情
getSaleOrder: async (id: number) => {
return await request.get({ url: `/oms/sale-order/get?id=` + id })
},
// 新增OMS销售订单主表
// 新增销售订单
createSaleOrder: async (data: SaleOrderVO) => {
return await request.post({ url: `/oms/sale-order/create`, data })
},
// 修改OMS销售订单主表
// 修改销售订单
updateSaleOrder: async (data: SaleOrderVO) => {
return await request.put({ url: `/oms/sale-order/update`, data })
},
// 删除OMS销售订单主表
// 删除销售订单
deleteSaleOrder: async (id: number) => {
return await request.delete({ url: `/oms/sale-order/delete?id=` + id })
},
// 导出OMS销售订单主表 Excel
// 导出销售订单 Excel
exportSaleOrder: async (params) => {
return await request.download({ url: `/oms/sale-order/export-excel`, params })
},
// ==================== 子表(OMS销售订单明细) ====================
// ==================== 子表(销售订单明细) ====================
// 获得OMS销售订单明细分页
getSaleOrderEntryPage: async (params) => {
return await request.get({ url: `/oms/sale-order/sale-order-entry/page`, params })
},
// 新增OMS销售订单明细
createSaleOrderEntry: async (data) => {
return await request.post({ url: `/oms/sale-order/sale-order-entry/create`, data })
},
// 修改OMS销售订单明细
updateSaleOrderEntry: async (data) => {
return await request.put({ url: `/oms/sale-order/sale-order-entry/update`, data })
},
// 删除OMS销售订单明细
deleteSaleOrderEntry: async (id: number) => {
return await request.delete({ url: `/oms/sale-order/sale-order-entry/delete?id=` + id })
},
// 获得OMS销售订单明细
getSaleOrderEntry: async (id: number) => {
return await request.get({ url: `/oms/sale-order/sale-order-entry/get?id=` + id })
// 获得销售订单明细列表
getSaleOrderEntryListByParentId: async (parentId) => {
return await request.get({ url: `/oms/sale-order/sale-order-entry/list-by-parent-id?parentId=` + parentId })
},
}

View File

@ -234,346 +234,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
noTagsView: true
}
},
{
path: '/bpm',
component: Layout,
name: 'bpm',
meta: {
hidden: true
},
children: [
{
path: 'manager/form/edit',
component: () => import('@/views/bpm/form/editor/index.vue'),
name: 'BpmFormEditor',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '设计流程表单',
activeMenu: '/bpm/manager/form'
}
},
{
path: 'manager/model/edit',
component: () => import('@/views/bpm/model/editor/index.vue'),
name: 'BpmModelEditor',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '设计流程',
activeMenu: '/bpm/manager/model'
}
},
{
path: 'manager/simple/workflow/model/edit',
component: () => import('@/views/bpm/simpleWorkflow/index.vue'),
name: 'SimpleWorkflowDesignEditor',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '仿钉钉设计流程',
activeMenu: '/bpm/manager/model'
}
},
{
path: 'manager/definition',
component: () => import('@/views/bpm/definition/index.vue'),
name: 'BpmProcessDefinition',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '流程定义',
activeMenu: '/bpm/manager/model'
}
},
{
path: 'process-instance/detail',
component: () => import('@/views/bpm/processInstance/detail/index.vue'),
name: 'BpmProcessInstanceDetail',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '流程详情',
activeMenu: '/bpm/task/my'
}
},
{
path: 'oa/leave/create',
component: () => import('@/views/bpm/oa/leave/create.vue'),
name: 'OALeaveCreate',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '发起 OA 请假',
activeMenu: '/bpm/oa/leave'
}
},
{
path: 'oa/leave/detail',
component: () => import('@/views/bpm/oa/leave/detail.vue'),
name: 'OALeaveDetail',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '查看 OA 请假',
activeMenu: '/bpm/oa/leave'
}
}
]
},
{
path: '/mall/product', // 商品中心
component: Layout,
name: 'ProductCenter',
meta: {
hidden: true
},
children: [
{
path: 'spu/add',
component: () => import('@/views/mall/product/spu/form/index.vue'),
name: 'ProductSpuAdd',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: 'ep:edit',
title: '商品添加',
activeMenu: '/mall/product/spu'
}
},
{
path: 'spu/edit/:id(\\d+)',
component: () => import('@/views/mall/product/spu/form/index.vue'),
name: 'ProductSpuEdit',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: 'ep:edit',
title: '商品编辑',
activeMenu: '/mall/product/spu'
}
},
{
path: 'spu/detail/:id(\\d+)',
component: () => import('@/views/mall/product/spu/form/index.vue'),
name: 'ProductSpuDetail',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: 'ep:view',
title: '商品详情',
activeMenu: '/mall/product/spu'
}
},
{
path: 'property/value/:propertyId(\\d+)',
component: () => import('@/views/mall/product/property/value/index.vue'),
name: 'ProductPropertyValue',
meta: {
noCache: true,
hidden: true,
canTo: true,
icon: 'ep:view',
title: '商品属性值',
activeMenu: '/product/property'
}
}
]
},
{
path: '/mall/trade', // 交易中心
component: Layout,
name: 'TradeCenter',
meta: {
hidden: true
},
children: [
{
path: 'order/detail/:id(\\d+)',
component: () => import('@/views/mall/trade/order/detail/index.vue'),
name: 'TradeOrderDetail',
meta: { title: '订单详情', icon: 'ep:view', activeMenu: '/mall/trade/order' }
},
{
path: 'after-sale/detail/:id(\\d+)',
component: () => import('@/views/mall/trade/afterSale/detail/index.vue'),
name: 'TradeAfterSaleDetail',
meta: { title: '退款详情', icon: 'ep:view', activeMenu: '/mall/trade/after-sale' }
}
]
},
{
path: '/member',
component: Layout,
name: 'MemberCenter',
meta: { hidden: true },
children: [
{
path: 'user/detail/:id',
name: 'MemberUserDetail',
meta: {
title: '会员详情',
noCache: true,
hidden: true
},
component: () => import('@/views/member/user/detail/index.vue')
}
]
},
{
path: '/pay',
component: Layout,
name: 'pay',
meta: { hidden: true },
children: [
{
path: 'cashier',
name: 'PayCashier',
meta: {
title: '收银台',
noCache: true,
hidden: true
},
component: () => import('@/views/pay/cashier/index.vue')
}
]
},
{
path: '/diy',
name: 'DiyCenter',
meta: { hidden: true },
component: Layout,
children: [
{
path: 'template/decorate/:id',
name: 'DiyTemplateDecorate',
meta: {
title: '模板装修',
noCache: true,
hidden: true,
activeMenu: '/mall/promotion/diy/template'
},
component: () => import('@/views/mall/promotion/diy/template/decorate.vue')
},
{
path: 'page/decorate/:id',
name: 'DiyPageDecorate',
meta: {
title: '页面装修',
noCache: true,
hidden: true,
activeMenu: '/mall/promotion/diy/page'
},
component: () => import('@/views/mall/promotion/diy/page/decorate.vue')
}
]
},
{
path: '/crm',
component: Layout,
name: 'CrmCenter',
meta: { hidden: true },
children: [
{
path: 'clue/detail/:id',
name: 'CrmClueDetail',
meta: {
title: '线索详情',
noCache: true,
hidden: true,
activeMenu: '/crm/clue'
},
component: () => import('@/views/crm/clue/detail/index.vue')
},
{
path: 'customer/detail/:id',
name: 'CrmCustomerDetail',
meta: {
title: '客户详情',
noCache: true,
hidden: true,
activeMenu: '/crm/customer'
},
component: () => import('@/views/crm/customer/detail/index.vue')
},
{
path: 'business/detail/:id',
name: 'CrmBusinessDetail',
meta: {
title: '商机详情',
noCache: true,
hidden: true,
activeMenu: '/crm/business'
},
component: () => import('@/views/crm/business/detail/index.vue')
},
{
path: 'contract/detail/:id',
name: 'CrmContractDetail',
meta: {
title: '合同详情',
noCache: true,
hidden: true,
activeMenu: '/crm/contract'
},
component: () => import('@/views/crm/contract/detail/index.vue')
},
{
path: 'receivable-plan/detail/:id',
name: 'CrmReceivablePlanDetail',
meta: {
title: '回款计划详情',
noCache: true,
hidden: true,
activeMenu: '/crm/receivable-plan'
},
component: () => import('@/views/crm/receivable/plan/detail/index.vue')
},
{
path: 'receivable/detail/:id',
name: 'CrmReceivableDetail',
meta: {
title: '回款详情',
noCache: true,
hidden: true,
activeMenu: '/crm/receivable'
},
component: () => import('@/views/crm/receivable/detail/index.vue')
},
{
path: 'contact/detail/:id',
name: 'CrmContactDetail',
meta: {
title: '联系人详情',
noCache: true,
hidden: true,
activeMenu: '/crm/contact'
},
component: () => import('@/views/crm/contact/detail/index.vue')
},
{
path: 'product/detail/:id',
name: 'CrmProductDetail',
meta: {
title: '产品详情',
noCache: true,
hidden: true,
activeMenu: '/crm/product'
},
component: () => import('@/views/crm/product/detail/index.vue')
}
]
}
]
]
export default remainingRouter

View File

@ -41,7 +41,7 @@ interface AppState {
export const useAppStore = defineStore('app', {
state: (): AppState => {
return {
userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
userInfo: 'userAdminInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
sizeMap: ['default', 'large', 'small'],
mobile: false, // 是否是移动端
title: import.meta.env.VITE_APP_TITLE, // 标题

View File

@ -184,9 +184,9 @@ const loginData = reactive({
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
loginForm: {
tenantName: '芋道源码',
username: 'admin',
password: 'admin123',
tenantName: '',
username: '',
password: '',
captchaVerification: '',
rememberMe: true //
}

View File

@ -0,0 +1,160 @@
<template>
<Dialog v-model="dialogVisible" title="品牌权限">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<!-- <el-form-item label="角色名称">
<el-tag>{{ formData.name }}</el-tag>
</el-form-item>
<el-form-item label="角色标识">
<el-tag>{{ formData.code }}</el-tag>
</el-form-item>-->
<el-form-item label="品牌权限">
<el-card class="cardHeight">
<!-- <template #header>
全选/全不选:
<el-switch
v-model="treeNodeAll"
active-text="是"
inactive-text="否"
inline-prompt
@change="handleCheckedTreeNodeAll"
/>
全部展开/折叠:
<el-switch
v-model="menuExpand"
active-text="展开"
inactive-text="折叠"
inline-prompt
@change="handleCheckedTreeExpand"
/>
</template>-->
<el-tree
ref="treeRef"
:data="menuOptions"
:props="defaultProps"
empty-text="加载中,请稍候"
node-key="id"
show-checkbox
/>
</el-card>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { defaultProps, handleTree } from '@/utils/tree'
import * as CustomerApi from '@/api/oms/customer'
import * as CustomerBrandApi from '@/api/oms/customerbrand'
import * as BrandApi from '@/api/oms/brand'
defineOptions({ name: 'SystemRoleAssignMenuForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const formData = reactive({
id: undefined,
name: '',
code: '',
menuIds: []
})
const formRef = ref() // Ref
const menuOptions = ref<any[]>([]) //
const menuExpand = ref(false) // /
const treeRef = ref() // Ref
const treeNodeAll = ref(false) // /
/** 打开弹窗 */
const open = async (row: RoleApi.RoleVO) => {
dialogVisible.value = true
resetForm()
// Menu setChecked
menuOptions.value = handleTree(await BrandApi.getSimpleBrandList())
//
formData.id = row.id
formData.name = row.name
formData.code = row.code
formLoading.value = true
try {
formData.value.menuIds = await CustomerBrandApi.getCustomerBrandList(row.id)
//
formData.value.menuIds.forEach((menuId: number) => {
treeRef.value.setChecked(menuId, true, false)
})
} finally {
formLoading.value = false
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = {
customerId: formData.id,
brandIds: [
...(treeRef.value.getCheckedKeys(false) as unknown as Array<number>), //
...(treeRef.value.getHalfCheckedKeys() as unknown as Array<number>) //
]
}
await CustomerBrandApi.assignCustomerBrand(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
//
treeNodeAll.value = false
menuExpand.value = false
//
formData.value = {
id: undefined,
name: '',
code: '',
menuIds: []
}
treeRef.value?.setCheckedNodes([])
formRef.value?.resetFields()
}
/** 全选/全不选 */
const handleCheckedTreeNodeAll = () => {
treeRef.value.setCheckedNodes(treeNodeAll.value ? menuOptions.value : [])
}
/** 展开/折叠全部 */
const handleCheckedTreeExpand = () => {
const nodes = treeRef.value?.store.nodesMap
for (let node in nodes) {
if (nodes[node].expanded === menuExpand.value) {
continue
}
nodes[node].expanded = menuExpand.value
}
}
</script>
<style lang="scss" scoped>
.cardHeight {
width: 100%;
max-height: 400px;
overflow-y: scroll;
}
</style>

View File

@ -10,6 +10,9 @@
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="公司" prop="company">
<el-input v-model="formData.company" placeholder="请输入公司" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item>
@ -33,7 +36,7 @@
<el-form-item label="数据状态" prop="status">
<el-switch
v-model="formData.status"
:active-text="formData.status==1?'用':'禁用'"
:active-text="formData.status==1?'用':'禁用'"
active-value="1"
inactive-value="0"
/>
@ -78,6 +81,7 @@ const formLoading = ref(false) // 表单的加载中1修改时的数据加
const formType = ref('') // create - update -
const formData = ref({
name: undefined,
company: undefined,
email: undefined,
contacts: undefined,
phone: undefined,
@ -88,6 +92,7 @@ const formData = ref({
})
const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
company: [{ required: true, message: '公司不能为空', trigger: 'blur' }],
contacts: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
phone: [{ required: true, message: '联系人手机号不能为空', trigger: 'blur' },
{
@ -168,6 +173,7 @@ const submitForm = async () => {
const resetForm = () => {
formData.value = {
name: undefined,
company: undefined,
email: undefined,
contacts: undefined,
phone: undefined,

View File

@ -26,6 +26,15 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item label="公司" prop="company">
<el-input
v-model="queryParams.company"
placeholder="请输入名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
v-model="queryParams.type"
@ -101,6 +110,7 @@
<el-table-column width="30" label="选择" type="selection" />
<el-table-column label="ID" align="center" prop="id" />
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="公司" align="center" prop="company" />
<el-table-column label="邮箱" align="center" prop="email" />
<el-table-column label="联系人" align="center" prop="contacts" />
<el-table-column label="联系人手机号" align="center" prop="phone" />
@ -125,9 +135,9 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<el-table-column label="操作" align="center" width="180px">
<template #default="scope">
<el-button
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
@ -135,6 +145,16 @@
>
编辑
</el-button>
<el-button
v-hasPermi="['oms:customer:update']"
link
preIcon="ep:basketball"
title="品牌权限"
type="primary"
@click="openAssignBrandForm(scope.row)"
>
品牌权限
</el-button>
<el-button
link
type="danger"
@ -157,6 +177,8 @@
<!-- 表单弹窗添加/修改 -->
<CustomerForm ref="formRef" @success="getList" />
<!-- 表单弹窗菜单权限 -->
<CustomerAssignBrandForm ref="assignBrandFormRef" @success="getList" />
</template>
<script setup lang="ts">
@ -165,7 +187,8 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CustomerApi, CustomerVO } from '@/api/oms/customer'
import CustomerForm from './CustomerForm.vue'
import CustomerAssignBrandForm from './CustomerAssignBrandForm.vue'
import RoleAssignMenuForm from "@/views/system/role/RoleAssignMenuForm.vue";
/** 客户 列表 */
defineOptions({ name: 'Customer' })
@ -180,6 +203,7 @@ const queryParams = reactive({
pageSize: 10,
id: undefined,
name: undefined,
company: undefined,
type: undefined,
status: undefined,
})
@ -216,6 +240,12 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 菜单权限操作 */
const assignBrandFormRef = ref()
const openAssignBrandForm = async (row: RoleApi.RoleVO) => {
assignBrandFormRef.value.open(row)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {

View File

@ -7,9 +7,6 @@
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="单据编号" prop="billno">
<el-input v-model="formData.billno" placeholder="请输入单据编号" />
</el-form-item>
<el-form-item label="客户id" prop="customerId">
<el-input v-model="formData.customerId" placeholder="请输入客户id" />
</el-form-item>
@ -22,23 +19,7 @@
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="更新时间" prop="udate">
<el-date-picker
v-model="formData.udate"
type="date"
value-format="x"
placeholder="选择更新时间"
/>
</el-form-item>
<el-form-item label="创建时间" prop="cdate">
<el-date-picker
v-model="formData.cdate"
type="date"
value-format="x"
placeholder="选择创建时间"
/>
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="确认日期" prop="confirmdate">
<el-date-picker
@ -56,17 +37,9 @@
placeholder="选择计划日期"
/>
</el-form-item>
<el-form-item label="订单类型" prop="orderType">
<el-select v-model="formData.orderType" placeholder="请选择订单类型">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="手机" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入手机" />
</el-form-item>
<el-form-item label="传真" prop="fax">
<el-input v-model="formData.fax" placeholder="请输入传真" />
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="formData.remarks" placeholder="请输入备注" />
</el-form-item>
@ -89,6 +62,12 @@
<el-input v-model="formData.invoiceRemarks" placeholder="请输入发票备注" />
</el-form-item>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="销售订单明细" name="saleOrderEntry">
<SaleOrderEntryForm ref="saleOrderEntryFormRef" :parent-id="formData.id" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
@ -97,8 +76,9 @@
</template>
<script setup lang="ts">
import { SaleOrderApi, SaleOrderVO } from '@/api/oms/saleorder'
import SaleOrderEntryForm from './components/SaleOrderEntryForm.vue'
/** OMS销售订单主表 表单 */
/** 销售订单 表单 */
defineOptions({ name: 'SaleOrderForm' })
const { t } = useI18n() //
@ -109,18 +89,12 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
billno: undefined,
customerId: undefined,
bizdate: undefined,
remark: undefined,
udate: undefined,
cdate: undefined,
confirmdate: undefined,
plansenddate: undefined,
orderType: undefined,
phone: undefined,
fax: undefined,
remarks: undefined,
emails: undefined,
invoiceCode: undefined,
@ -133,6 +107,10 @@ const formRules = reactive({
})
const formRef = ref() // Ref
/** 子表的表单 */
const subTabsName = ref('saleOrderEntry')
const saleOrderEntryFormRef = ref()
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
@ -156,10 +134,19 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
const submitForm = async () => {
//
await formRef.value.validate()
//
try {
await saleOrderEntryFormRef.value.validate()
} catch (e) {
subTabsName.value = 'saleOrderEntry'
return
}
//
formLoading.value = true
try {
const data = formData.value as unknown as SaleOrderVO
//
data.saleOrderEntrys = saleOrderEntryFormRef.value.getData()
if (formType.value === 'create') {
await SaleOrderApi.createSaleOrder(data)
message.success(t('common.createSuccess'))
@ -178,18 +165,12 @@ const submitForm = async () => {
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
billno: undefined,
customerId: undefined,
bizdate: undefined,
remark: undefined,
udate: undefined,
cdate: undefined,
confirmdate: undefined,
plansenddate: undefined,
orderType: undefined,
phone: undefined,
fax: undefined,
remarks: undefined,
emails: undefined,
invoiceCode: undefined,

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