Commit 95edd1d4514db554548c3a4a55ad765a2212ff2a

Authored by YunaiV
2 parents 414c1be1 418ae829

Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-redis

 Conflicts:
	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java
	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java
Showing 243 changed files with 3417 additions and 1230 deletions

Too many changes.

To preserve performance only 100 of 243 files are displayed.

Docker-HOWTO.md
1 1 # Docker Build & Up
2 2  
3 3 目标: 快速部署体验系统,帮助了解系统之间的依赖关系。
  4 +依赖:docker compose v2,删除`name: yudao-system`,降低`version`版本为`3.3`以下,支持`docker-compose`。
4 5  
5 6 ## 功能文件列表
6 7  
7 8 ```text
8 9 .
9   -├── Docker-HOWTO.md
10   -├── docker-compose.yml
11   -├── docker.env
  10 +├── Docker-HOWTO.md
  11 +├── docker-compose.yml
  12 +├── docker.env <-- 提供docker-compose环境变量配置
12 13 ├── yudao-server
13   -│ ├── Dockerfile
14   -│ └── nginx.conf
  14 +│ └── Dockerfile
15 15 └── yudao-ui-admin
16 16 ├── .dockerignore
17   - └── Dockerfile
  17 + ├── Dockerfile
  18 + └── nginx.conf <-- 提供基础配置,gzip压缩、api转发
18 19 ```
19 20  
20   -## Maven build (Optional)
  21 +## 构建 jar 包
21 22  
22 23 ```shell
23 24 # 创建maven缓存volume
... ... @@ -30,29 +31,19 @@ docker run -it --rm --name yudao-maven \
30 31 maven mvn clean install package '-Dmaven.test.skip=true'
31 32 ```
32 33  
33   -## Docker Compose Build
34   -
35   -```shell
36   -docker compose --env-file docker.env build
37   -```
38   -
39   -## Docker Compose Up
  34 +## 构建启动服务
40 35  
41 36 ```shell
42 37 docker compose --env-file docker.env up -d
43 38 ```
44 39  
45   -第一次执行,由于数据库未初始化,因此yudao-server容器会运行失败。执行如下命令初始化数据库:
  40 +首次运行会自动构建容器。可以通过`docker compose build [service]`来手动构建所有或某个docker镜像
46 41  
47   -```shell
48   -docker compose exec -T mysql \
49   - sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" --default-character-set=utf8mb4 ruoyi-vue-pro' \
50   - < ./sql/mysql/ruoyi-vue-pro.sql
51   -```
  42 +`--env-file docker.env`为可选参数,只是展示了通过`.env`文件配置容器启动的环境变量,`docker-compose.yml`本身已经提供足够的默认参数来正常运行系统。
52 43  
53   -## Server:Port
  44 +## 服务器的宿主机端口映射
54 45  
55   -- admin: http://localhost:8080
56   -- API: http://localhost:48080
57   -- mysql: root/123456, port: 3308
  46 +- admin ui: http://localhost:8080
  47 +- api server: http://localhost:48080
  48 +- mysql: root/123456, port: 3306
58 49 - redis: port: 6379
... ...
README.md
... ... @@ -102,7 +102,7 @@
102 102  
103 103 系统内置多种多种业务功能,可以用于快速你的业务系统:
104 104  
105   -![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png)
  105 +![功能分层](https://static.iocoder.cn/ruoyi-vue-pro-biz.png?imageView2/2/format/webp)
106 106  
107 107 * 系统功能
108 108 * 基础设施
... ...
docker-compose.yml
1   -version: "3.8"
  1 +version: "3.4"
2 2  
3 3 name: yudao-system
4 4  
... ... @@ -9,25 +9,22 @@ services:
9 9 restart: unless-stopped
10 10 tty: true
11 11 ports:
12   - - 13306:3306
  12 + - "3306:3306"
13 13 environment:
14 14 MYSQL_DATABASE: ${MYSQL_DATABASE:-ruoyi-vue-pro}
15 15 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-123456}
16 16 volumes:
17 17 - mysql:/var/lib/mysql/
18   - networks:
19   - - yudao-network
  18 + - ./sql/mysql/ruoyi-vue-pro.sql:/docker-entrypoint-initdb.d/ruoyi-vue-pro.sql:ro
20 19  
21 20 redis:
22 21 container_name: yudao-redis
23 22 image: redis:6-alpine
24 23 restart: unless-stopped
25 24 ports:
26   - - 16379:6379
  25 + - "6379:6379"
27 26 volumes:
28 27 - redis:/data
29   - networks:
30   - - yudao-network
31 28  
32 29 server:
33 30 container_name: yudao-server
... ... @@ -36,7 +33,7 @@ services:
36 33 image: yudao-server
37 34 restart: unless-stopped
38 35 ports:
39   - - 48080:48080
  36 + - "48080:48080"
40 37 environment:
41 38 # https://github.com/polovyivan/docker-pass-configs-to-container
42 39 SPRING_PROFILES_ACTIVE: local
... ... @@ -54,8 +51,6 @@ services:
54 51 --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root}
55 52 --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456}
56 53 --spring.redis.host=${REDIS_HOST:-yudao-redis}
57   - networks:
58   - - yudao-network
59 54 depends_on:
60 55 - mysql
61 56 - redis
... ... @@ -78,16 +73,10 @@ services:
78 73 image: yudao-admin
79 74 restart: unless-stopped
80 75 ports:
81   - - 8080:80
82   - networks:
83   - - yudao-network
  76 + - "8080:80"
84 77 depends_on:
85 78 - server
86 79  
87   -networks:
88   - yudao-network:
89   - driver: bridge
90   -
91 80 volumes:
92 81 mysql:
93 82 driver: local
... ...
... ... @@ -30,7 +30,7 @@
30 30 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
31 31  
32 32 <properties>
33   - <revision>1.7.3-snapshot</revision>
  33 + <revision>1.8.0-snapshot</revision>
34 34 <!-- Maven 相关 -->
35 35 <java.version>1.8</java.version>
36 36 <maven.compiler.source>${java.version}</maven.compiler.source>
... ...
yudao-dependencies/pom.xml
... ... @@ -14,7 +14,7 @@
14 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15  
16 16 <properties>
17   - <revision>1.7.3-snapshot</revision>
  17 + <revision>1.8.0-snapshot</revision>
18 18 <!-- 统一依赖管理 -->
19 19 <spring.boot.version>2.7.13</spring.boot.version>
20 20 <!-- Web 相关 -->
... ... @@ -71,7 +71,7 @@
71 71 <justauth.version>1.0.1</justauth.version>
72 72 <jimureport.version>1.5.8</jimureport.version>
73 73 <xercesImpl.version>2.12.2</xercesImpl.version>
74   - <wx-java-mp.version>4.5.0</wx-java-mp.version>
  74 + <weixin-java.version>4.5.0</weixin-java.version>
75 75 </properties>
76 76  
77 77 <dependencyManagement>
... ... @@ -216,10 +216,9 @@
216 216 <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
217 217 <version>${dynamic-datasource.version}</version>
218 218 </dependency>
219   -
220 219 <dependency>
221 220 <groupId>com.github.yulichang</groupId>
222   - <artifactId>mybatis-plus-join-boot-starter</artifactId>
  221 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
223 222 <version>${mybatis-plus-join-boot-starter.version}</version>
224 223 </dependency>
225 224  
... ... @@ -601,8 +600,23 @@
601 600  
602 601 <dependency>
603 602 <groupId>com.github.binarywang</groupId>
  603 + <artifactId>weixin-java-pay</artifactId>
  604 + <version>${weixin-java.version}</version>
  605 + </dependency>
  606 + <dependency>
  607 + <groupId>com.github.binarywang</groupId>
  608 + <artifactId>weixin-java-mp</artifactId>
  609 + <version>${weixin-java.version}</version>
  610 + </dependency>
  611 + <dependency>
  612 + <groupId>com.github.binarywang</groupId>
604 613 <artifactId>wx-java-mp-spring-boot-starter</artifactId>
605   - <version>${wx-java-mp.version}</version>
  614 + <version>${weixin-java.version}</version>
  615 + </dependency>
  616 + <dependency>
  617 + <groupId>com.github.binarywang</groupId>
  618 + <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
  619 + <version>${weixin-java.version}</version>
606 620 </dependency>
607 621  
608 622 <!-- 积木报表-->
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
... ... @@ -27,8 +27,6 @@ public interface WebFilterOrderEnum {
27 27  
28 28 int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
29 29  
30   - int ACTIVITI_FILTER = -98; // 需要保证在 Spring Security 过滤后面
31   -
32 30 int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
33 31  
34 32 int DEMO_FILTER = Integer.MAX_VALUE;
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java
... ... @@ -37,15 +37,4 @@ public interface GlobalErrorCodeConstants {
37 37  
38 38 ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
39 39  
40   - /**
41   - * 是否为服务端错误,参考 HTTP 5XX 错误码段
42   - *
43   - * @param code 错误码
44   - * @return 是否
45   - */
46   - static boolean isServerErrorCode(Integer code) {
47   - return code != null
48   - && code >= INTERNAL_SERVER_ERROR.getCode() && code <= INTERNAL_SERVER_ERROR.getCode() + 99;
49   - }
50   -
51 40 }
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
1 1 package cn.iocoder.yudao.framework.common.pojo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4   -import cn.iocoder.yudao.framework.common.exception.ServerException;
5 4 import cn.iocoder.yudao.framework.common.exception.ServiceException;
6 5 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
7 6 import com.fasterxml.jackson.annotation.JsonIgnore;
... ... @@ -92,10 +91,6 @@ public class CommonResult&lt;T&gt; implements Serializable {
92 91 if (isSuccess()) {
93 92 return;
94 93 }
95   - // 服务端异常
96   - if (GlobalErrorCodeConstants.isServerErrorCode(code)) {
97   - throw new ServerException(code, msg);
98   - }
99 94 // 业务异常
100 95 throw new ServiceException(code, msg);
101 96 }
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
... ... @@ -164,7 +164,7 @@ public class CollectionUtils {
164 164 return from.stream().filter(predicate).findFirst().orElse(null);
165 165 }
166 166  
167   - public static <T, V extends Comparable<? super V>> V getMaxValue(List<T> from, Function<T, V> valueFunc) {
  167 + public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
168 168 if (CollUtil.isEmpty(from)) {
169 169 return null;
170 170 }
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.date.LocalDateTimeUtil;
4 4  
5 5 import java.time.Duration;
6 6 import java.time.LocalDateTime;
  7 +import java.time.LocalTime;
7 8  
8 9 /**
9 10 * 时间工具类,用于 {@link java.time.LocalDateTime}
... ... @@ -50,7 +51,7 @@ public class LocalDateTimeUtils {
50 51 * 判断当前时间是否在该时间范围内
51 52 *
52 53 * @param startTime 开始时间
53   - * @param endTime 结束时间
  54 + * @param endTime 结束时间
54 55 * @return 是否
55 56 */
56 57 public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
... ... @@ -60,4 +61,24 @@ public class LocalDateTimeUtils {
60 61 return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
61 62 }
62 63  
  64 + /**
  65 + * 检查时间重叠 不包含日期
  66 + *
  67 + * @param startTime1 需要校验的开始时间
  68 + * @param endTime1 需要校验的结束时间
  69 + * @param startTime2 校验所需的开始时间
  70 + * @param endTime2 校验所需的结束时间
  71 + * @return 是否重叠
  72 + */
  73 + // TODO @puhui999:LocalDateTimeUtil.isOverlap() 是不是可以满足呀?
  74 + public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
  75 + // 判断时间是否重叠
  76 + // 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 []
  77 + return startTime1.isBefore(endTime2) && endTime1.isAfter(startTime2)
  78 + // 开始时间在已配置时段的开始时间之前 且 结束时间在已配置时段的开始时间之后 (] 或 ()
  79 + || startTime1.isBefore(startTime2) && endTime1.isAfter(startTime2)
  80 + // 开始时间在已配置时段的结束时间之前 且 结束时间在已配值时段的结束时间之后 [) 或 ()
  81 + || startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2);
  82 + }
  83 +
63 84 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRuleTest.java
... ... @@ -4,11 +4,11 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ReflectUtil;
5 5 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
6 6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
7   -import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
8   -import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
9 7 import cn.iocoder.yudao.framework.security.core.LoginUser;
10 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
11 9 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  10 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  11 +import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
12 12 import net.sf.jsqlparser.expression.Alias;
13 13 import net.sf.jsqlparser.expression.Expression;
14 14 import org.junit.jupiter.api.BeforeEach;
... ... @@ -23,6 +23,7 @@ import static cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataP
23 23 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
24 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
25 25 import static org.junit.jupiter.api.Assertions.*;
  26 +import static org.mockito.ArgumentMatchers.eq;
26 27 import static org.mockito.ArgumentMatchers.same;
27 28 import static org.mockito.Mockito.mockStatic;
28 29 import static org.mockito.Mockito.when;
... ... @@ -73,6 +74,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
73 74 LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
74 75 .setUserType(UserTypeEnum.ADMIN.getValue()));
75 76 securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
  77 + // mock 方法(permissionApi 返回 null)
  78 + when(permissionApi.getDeptDataPermission(eq(loginUser.getId()))).thenReturn(null);
76 79  
77 80 // 调用
78 81 NullPointerException exception = assertThrows(NullPointerException.class,
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
... ... @@ -63,9 +63,7 @@
63 63 <dependency>
64 64 <groupId>com.github.binarywang</groupId>
65 65 <artifactId>weixin-java-pay</artifactId>
66   - <version>4.5.0</version>
67 66 </dependency>
68   - <!-- TODO 芋艿:清理 -->
69 67  
70 68 <!-- Test 测试相关 -->
71 69 <dependency>
... ...
yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml
... ... @@ -34,16 +34,12 @@
34 34 <!-- 三方云服务相关 -->
35 35 <dependency>
36 36 <groupId>com.github.binarywang</groupId>
37   -<!-- <artifactId>weixin-java-mp</artifactId>-->
38 37 <artifactId>wx-java-mp-spring-boot-starter</artifactId>
39   - <version>4.5.0</version>
40 38 </dependency>
41 39 <dependency>
42 40 <groupId>com.github.binarywang</groupId>
43 41 <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
44   - <version>4.5.0</version>
45 42 </dependency>
46   - <!-- TODO 芋艿:清理 -->
47 43 </dependencies>
48 44  
49 45 </project>
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
... ... @@ -64,9 +64,8 @@
64 64  
65 65 <dependency>
66 66 <groupId>com.github.yulichang</groupId>
67   - <artifactId>mybatis-plus-join-boot-starter</artifactId>
  67 + <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
68 68 </dependency>
69   -
70 69 </dependencies>
71 70  
72 71 </project>
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -6,10 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 6 import com.baomidou.mybatisplus.core.conditions.Wrapper;
7 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 8 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
9 10 import com.baomidou.mybatisplus.core.metadata.IPage;
10 11 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
11 12 import com.baomidou.mybatisplus.extension.toolkit.Db;
12   -import com.github.yulichang.base.MPJBaseMapper;
13 13 import org.apache.ibatis.annotations.Param;
14 14  
15 15 import java.util.Collection;
... ... @@ -17,10 +17,8 @@ import java.util.List;
17 17  
18 18 /**
19 19 * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
20   - * <p>
21   - * 为什么继承 MPJBaseMapper 接口?支持 MyBatis Plus 多表 Join 的能力。
22 20 */
23   -public interface BaseMapperX<T> extends MPJBaseMapper<T> {
  21 +public interface BaseMapperX<T> extends BaseMapper<T> {
24 22  
25 23 default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
26 24 // MyBatis Plus 查询
... ... @@ -46,18 +44,6 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
46 44 return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
47 45 }
48 46  
49   - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
50   - SFunction<T, ?> field3, Object value3) {
51   - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
52   - .eq(field3, value3));
53   - }
54   -
55   - default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
56   - SFunction<T, ?> field3, Object value3, SFunction<T, ?> field4, Object value4) {
57   - return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
58   - .eq(field3, value3).eq(field4, value4));
59   - }
60   -
61 47 default Long selectCount() {
62 48 return selectCount(new QueryWrapper<T>());
63 49 }
... ... @@ -117,26 +103,15 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
117 103 update(update, new QueryWrapper<>());
118 104 }
119 105  
120   - /**
121   - * 根据ID 批量更新,适合大量数据更新
122   - *
123   - * @param entities 实体们
124   - */
125 106 default void updateBatch(Collection<T> entities) {
126 107 Db.updateBatchById(entities);
127 108 }
128   -
129 109 default void updateBatch(Collection<T> entities, int size) {
130 110 Db.updateBatchById(entities, size);
131 111 }
132 112  
133   - /**
134   - * 批量修改插入, 会根据实体的主键是否为空,更新还是修改。默认为 1000
135   - *
136   - * @param entities 实体们
137   - */
138   - default void saveOrUpdateBatch(Collection<T> entities){
139   - Db.saveOrUpdateBatch(entities);
  113 + default void saveOrUpdateBatch(Collection<T> collection) {
  114 + Db.saveOrUpdateBatch(collection);
140 115 }
141 116  
142 117 }
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
... ... @@ -60,7 +60,7 @@ public class YudaoWebSecurityConfigurerAdapter {
60 60 /**
61 61 * 自定义的权限映射 Bean 们
62 62 *
63   - * @see #configure(HttpSecurity)
  63 + * @see #filterChain(HttpSecurity)
64 64 */
65 65 @Resource
66 66 private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;
... ... @@ -79,7 +79,7 @@ public class YudaoWebSecurityConfigurerAdapter {
79 79  
80 80 /**
81 81 * 配置 URL 的安全配置
82   - * <p>
  82 + *
83 83 * anyRequest | 匹配所有请求路径
84 84 * access | SpringEl表达式结果为true时可以访问
85 85 * anonymous | 匿名可以访问
... ... @@ -141,7 +141,6 @@ public class YudaoWebSecurityConfigurerAdapter {
141 141  
142 142 // 添加 Token Filter
143 143 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
144   -
145 144 return httpSecurity.build();
146 145 }
147 146  
... ...
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java
... ... @@ -40,6 +40,7 @@ public class BaseDbAndRedisUnitTest {
40 40  
41 41 // Redis 配置类
42 42 RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
  43 +// RedisAutoConfiguration.class, // Spring Redis 自动配置类
43 44 YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
44 45 RedissonAutoConfiguration.class, // Redisson 自动高配置类
45 46 })
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
... ... @@ -21,6 +21,7 @@ public class YudaoJacksonAutoConfiguration {
21 21 @Bean
22 22 public BeanPostProcessor objectMapperBeanPostProcessor() {
23 23 return new BeanPostProcessor() {
  24 +
24 25 @Override
25 26 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
26 27 if (!(bean instanceof ObjectMapper)) {
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
... ... @@ -52,7 +52,8 @@ public class YudaoSwaggerAutoConfiguration {
52 52 // 接口信息
53 53 .info(buildInfo(properties))
54 54 // 接口安全配置
55   - .components(new Components().securitySchemes(securitySchemas));
  55 + .components(new Components().securitySchemes(securitySchemas))
  56 + .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));
56 57 securitySchemas.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
57 58 return openAPI;
58 59 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
... ... @@ -47,7 +47,7 @@ public class BpmTaskController {
47 47 @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
48 48 @PreAuthorize("@ss.hasPermission('bpm:task:query')")
49 49 public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
50   - @RequestParam("processInstanceId") String processInstanceId) {
  50 + @RequestParam("processInstanceId") String processInstanceId) {
51 51 return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
52 52 }
53 53  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
... ... @@ -17,12 +17,9 @@ import org.flowable.task.api.Task;
17 17 import org.flowable.task.api.history.HistoricTaskInstance;
18 18 import org.mapstruct.*;
19 19 import org.mapstruct.factory.Mappers;
20   -import org.springframework.beans.BeanUtils;
21 20  
22   -import java.util.Collections;
23 21 import java.util.List;
24 22 import java.util.Map;
25   -import java.util.stream.Collectors;
26 23  
27 24 /**
28 25 * Bpm 任务 Convert
... ... @@ -34,37 +31,9 @@ public interface BpmTaskConvert {
34 31  
35 32 BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
36 33  
37   - /**
38   - * 复制对象
39   - *
40   - * @param source 源 要复制的对象
41   - * @param target 目标 复制到此对象
42   - * @param <T>
43   - *
44   - * @return
45   - */
46   - public static <T> T copy(Object source, Class<T> target) {
47   - if (source == null || target == null) {
48   - return null;
49   - }
50   - try {
51   - T newInstance = target.getDeclaredConstructor().newInstance();
52   - BeanUtils.copyProperties(source, newInstance);
53   - return newInstance;
54   - } catch (Exception e) {
55   - throw new RuntimeException(e);
56   - }
57   - }
58   -
59   - default <T, K> List<K> copyList(List<T> source, Class<K> target) {
60   - if (null == source || source.isEmpty()) {
61   - return Collections.emptyList();
62   - }
63   - return source.stream().map(e -> copy(e, target)).collect(Collectors.toList());
64   - }
65   -
66 34 default List<BpmTaskTodoPageItemRespVO> convertList1(List<Task> tasks,
67   - Map<String, ProcessInstance> processInstanceMap, Map<Long, AdminUserRespDTO> userMap) {
  35 + Map<String, ProcessInstance> processInstanceMap,
  36 + Map<Long, AdminUserRespDTO> userMap) {
68 37 return CollectionUtils.convertList(tasks, task -> {
69 38 BpmTaskTodoPageItemRespVO respVO = convert1(task);
70 39 ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
... ... @@ -104,11 +73,13 @@ public interface BpmTaskConvert {
104 73  
105 74 BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);
106 75  
107   - @Mappings({@Mapping(source = "processInstance.id", target = "id"),
108   - @Mapping(source = "processInstance.name", target = "name"),
109   - @Mapping(source = "processInstance.startUserId", target = "startUserId"),
110   - @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
111   - @Mapping(source = "startUser.nickname", target = "startUserNickname")})
  76 + @Mappings({
  77 + @Mapping(source = "processInstance.id", target = "id"),
  78 + @Mapping(source = "processInstance.name", target = "name"),
  79 + @Mapping(source = "processInstance.startUserId", target = "startUserId"),
  80 + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
  81 + @Mapping(source = "startUser.nickname", target = "startUserNickname")
  82 + })
112 83 BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
113 84  
114 85 default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmActivityDO.java deleted
1   -package cn.iocoder.yudao.module.bpm.dal.dataobject.task;
2   -
3   -import com.fasterxml.jackson.annotation.JsonFormat;
4   -import lombok.*;
5   -import org.springframework.format.annotation.DateTimeFormat;
6   -
7   -import java.time.LocalDateTime;
8   -
9   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
11   -
12   -/**
13   - * 任务流程关联表
14   - *
15   - * @author kemengkai
16   - * @create 2022-05-09 10:33
17   - */
18   -@Data
19   -@AllArgsConstructor
20   -@NoArgsConstructor
21   -public class BpmActivityDO {
22   -
23   - /**
24   - * 任务流程关联id
25   - */
26   - private String id;
27   -
28   - /**
29   - * 审批结果
30   - */
31   - private Integer rev;
32   -
33   - /**
34   - * 任务流程部署id
35   - */
36   - private String procDefId;
37   -
38   - /**
39   - * 任务流程id
40   - */
41   - private String processInstanceId;
42   -
43   - /**
44   - * 任务执行id
45   - */
46   - private String executionId;
47   -
48   - /**
49   - * 任务key
50   - */
51   - private String activityId;
52   -
53   - /**
54   - * 任务id
55   - */
56   - private String taskId;
57   -
58   - /**
59   - * 调用流程id
60   - */
61   - private String callProcInstId;
62   -
63   - /**
64   - * 任务名称
65   - */
66   - private String activityName;
67   -
68   - /**
69   - * 任务类型
70   - */
71   - private String activityType;
72   -
73   - /**
74   - * 任务审批人id
75   - */
76   - private String assignee;
77   -
78   - /**
79   - * 任务开始时间
80   - */
81   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
82   - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
83   - private LocalDateTime startTime;
84   -
85   - /**
86   - * 任务结束时间
87   - */
88   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
89   - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
90   - private LocalDateTime endTime;
91   -
92   - private Integer transactionOrder;
93   -
94   - private LocalDateTime duration;
95   -
96   - /**
97   - * 删除结果
98   - */
99   - private String deleteReason;
100   -
101   - /**
102   - * 租户id
103   - */
104   - private String tenantId;
105   -}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
... ... @@ -15,8 +15,8 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert;
15 15 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
16 16 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
17 17 import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
18   -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
19 18 import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants;
  19 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum;
20 20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript;
21 21 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
22 22 import cn.iocoder.yudao.module.system.api.dept.PostApi;
... ... @@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
89 89  
90 90 @Override
91 91 public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
92   - String taskDefinitionKey) {
  92 + String taskDefinitionKey) {
93 93 return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey);
94 94 }
95 95  
... ... @@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
128 128 validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions());
129 129 // 校验是否已经配置
130 130 BpmTaskAssignRuleDO existRule =
131   - taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
  131 + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey());
132 132 if (existRule != null) {
133 133 throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey());
134 134 }
135 135  
136 136 // 存储
137 137 BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)
138   - .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
  138 + .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建
139 139 taskRuleMapper.insert(rule);
140 140 return rule.getId();
141 141 }
... ... @@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
169 169  
170 170 // 遍历,匹配对应的规则
171 171 Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap =
172   - CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
  172 + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey);
173 173 for (BpmTaskAssignRuleRespVO modelRule : modelRules) {
174 174 BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey());
175 175 if (processInstanceRule == null) {
176 176 return false;
177 177 }
178 178 if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal(
179   - modelRule.getOptions(), processInstanceRule.getOptions())) {
  179 + modelRule.getOptions(), processInstanceRule.getOptions())) {
180 180 return false;
181 181 }
182 182 }
... ... @@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
192 192 // 开始复制
193 193 List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules);
194 194 newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null)
195   - .setUpdateTime(null));
  195 + .setUpdateTime(null));
196 196 taskRuleMapper.insertBatch(newRules);
197 197 }
198 198  
... ... @@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
215 215 if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) {
216 216 roleApi.validRoleList(options);
217 217 } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
218   - BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
  218 + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
219 219 deptApi.validateDeptList(options);
220 220 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
221 221 postApi.validPostList(options);
... ... @@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
225 225 userGroupService.validUserGroups(options);
226 226 } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
227 227 dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT,
228   - CollectionUtils.convertSet(options, String::valueOf));
  228 + CollectionUtils.convertSet(options, String::valueOf));
229 229 } else {
230 230 throw new IllegalArgumentException(format("未知的规则类型({})", type));
231 231 }
... ... @@ -298,7 +298,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
298 298 }
299 299  
300 300 private Set<Long> calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) {
301   - List<AdminUserRespDTO> users = adminUserApi.getUsersByPostIds(rule.getOptions());
  301 + List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(rule.getOptions());
302 302 return convertSet(users, AdminUserRespDTO::getId);
303 303 }
304 304  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1   -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } }
2 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } }
3 3 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java
... ... @@ -40,6 +40,8 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
40 40 // mock 方法(startUser)
41 41 AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
42 42 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
  43 + // mock 方法(getStartUserDept)没有部门
  44 + when(deptApi.getDept(eq(10L))).thenReturn(null);
43 45  
44 46 // 调用
45 47 Set<Long> result = script.calculateTaskCandidateUsers(execution);
... ... @@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest {
56 58 when(adminUserApi.getUser(eq(1L))).thenReturn(startUser);
57 59 DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)
58 60 .setLeaderUserId(20L));
  61 + // mock 方法(getDept)
59 62 when(deptApi.getDept(eq(10L))).thenReturn(startUserDept);
  63 + when(deptApi.getDept(eq(100L))).thenReturn(null);
60 64  
61 65 // 调用
62 66 Set<Long> result = script.calculateTaskCandidateUsers(execution);
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImplTest.java
... ... @@ -120,7 +120,7 @@ public class BpmTaskAssignRuleServiceImplTest extends BaseDbUnitTest {
120 120 // mock 方法
121 121 List<AdminUserRespDTO> users = CollectionUtils.convertList(asSet(11L, 22L),
122 122 id -> new AdminUserRespDTO().setId(id));
123   - when(adminUserApi.getUsersByPostIds(eq(rule.getOptions()))).thenReturn(users);
  123 + when(adminUserApi.getUserListByPostIds(eq(rule.getOptions()))).thenReturn(users);
124 124 mockGetUserMap(asSet(11L, 22L));
125 125  
126 126 // 调用
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java
... ... @@ -23,10 +23,10 @@ import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgn
23 23 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;
24 24  
25 25 /**
26   -* {@link BpmUserGroupServiceImpl} 的单元测试类
27   -*
28   -* @author 芋道源码
29   -*/
  26 + * {@link BpmUserGroupServiceImpl} 的单元测试类
  27 + *
  28 + * @author 芋道源码
  29 + */
30 30 @Import(BpmUserGroupServiceImpl.class)
31 31 public class BpmUserGroupServiceTest extends BaseDbUnitTest {
32 32  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6   -@Schema(description = "管理后台 - 代码生成预览 Response VO,注意,每个文件都是一个该对象")
  6 +@Schema(description = "管理后台 - 代码生成预览 Response VO,注意,每个文件都是一个该对象")
7 7 @Data
8 8 public class CodegenPreviewRespVO {
9 9  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnBaseVO.java
... ... @@ -70,7 +70,7 @@ public class CodegenColumnBaseVO {
70 70 @NotNull(message = "是否为 List 查询操作的字段不能为空")
71 71 private Boolean listOperation;
72 72  
73   - @Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
  73 + @Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
74 74 @NotNull(message = "List 查询操作的条件类型不能为空")
75 75 private String listOperationCondition;
76 76  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableBaseVO.java
... ... @@ -12,7 +12,7 @@ import javax.validation.constraints.NotNull;
12 12 @Data
13 13 public class CodegenTableBaseVO {
14 14  
15   - @Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + @Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 16 @NotNull(message = "导入类型不能为空")
17 17 private Integer scene;
18 18  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java
... ... @@ -17,13 +17,13 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @ToString(callSuper = true)
18 18 public class CodegenTablePageReqVO extends PageParam {
19 19  
20   - @Schema(description = "表名称,模糊匹配", example = "yudao")
  20 + @Schema(description = "表名称,模糊匹配", example = "yudao")
21 21 private String tableName;
22 22  
23   - @Schema(description = "表描述,模糊匹配", example = "芋道")
  23 + @Schema(description = "表描述,模糊匹配", example = "芋道")
24 24 private String tableComment;
25 25  
26   - @Schema(description = "实体,模糊匹配", example = "Yudao")
  26 + @Schema(description = "实体,模糊匹配", example = "Yudao")
27 27 private String className;
28 28  
29 29 @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigExportReqVO.java
... ... @@ -15,10 +15,10 @@ public class ConfigExportReqVO {
15 15 @Schema(description = "参数名称", example = "模糊匹配")
16 16 private String name;
17 17  
18   - @Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
  18 + @Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
19 19 private String key;
20 20  
21   - @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
  21 + @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
22 22 private Integer type;
23 23  
24 24 @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java
... ... @@ -17,13 +17,13 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @ToString(callSuper = true)
18 18 public class ConfigPageReqVO extends PageParam {
19 19  
20   - @Schema(description = "数据源名称,模糊匹配", example = "名称")
  20 + @Schema(description = "数据源名称,模糊匹配", example = "名称")
21 21 private String name;
22 22  
23   - @Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
  23 + @Schema(description = "参数键名,模糊匹配", example = "yunai.db.username")
24 24 private String key;
25 25  
26   - @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
  26 + @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", example = "1")
27 27 private Integer type;
28 28  
29 29 @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java
... ... @@ -21,7 +21,7 @@ public class ConfigRespVO extends ConfigBaseVO {
21 21 @Size(max = 100, message = "参数键名长度不能超过100个字符")
22 22 private String key;
23 23  
24   - @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  24 + @Schema(description = "参数类型,参见 SysConfigTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
25 25 private Integer type;
26 26  
27 27 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigCreateReqVO.java
... ... @@ -14,7 +14,7 @@ import java.util.Map;
14 14 @ToString(callSuper = true)
15 15 public class FileConfigCreateReqVO extends FileConfigBaseVO {
16 16  
17   - @Schema(description = "存储器,参见 FileStorageEnum 枚举类参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + @Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
18 18 @NotNull(message = "存储器不能为空")
19 19 private Integer storage;
20 20  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java
... ... @@ -23,8 +23,8 @@ public class FileConfigPageReqVO extends PageParam {
23 23 @Schema(description = "存储器", example = "1")
24 24 private Integer storage;
25 25  
  26 + @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
26 27 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
27   - @Schema(description = "创建时间")
28 28 private LocalDateTime[] createTime;
29 29  
30 30 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java
... ... @@ -18,7 +18,7 @@ public class FileConfigRespVO extends FileConfigBaseVO {
18 18 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
19 19 private Long id;
20 20  
21   - @Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  21 + @Schema(description = "存储器,参见 FileStorageEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
22 22 @NotNull(message = "存储器不能为空")
23 23 private Integer storage;
24 24  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java
... ... @@ -17,14 +17,14 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @ToString(callSuper = true)
18 18 public class FilePageReqVO extends PageParam {
19 19  
20   - @Schema(description = "文件路径,模糊匹配", example = "yudao")
  20 + @Schema(description = "文件路径,模糊匹配", example = "yudao")
21 21 private String path;
22 22  
23   - @Schema(description = "文件类型,模糊匹配", example = "application/octet-stream")
  23 + @Schema(description = "文件类型,模糊匹配", example = "jpg")
24 24 private String type;
25 25  
  26 + @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
26 27 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
27   - @Schema(description = "创建时间")
28 28 private LocalDateTime[] createTime;
29 29  
30 30 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobPageReqVO.java
... ... @@ -12,13 +12,13 @@ import lombok.ToString;
12 12 @ToString(callSuper = true)
13 13 public class JobPageReqVO extends PageParam {
14 14  
15   - @Schema(description = "任务名称,模糊匹配", example = "测试任务")
  15 + @Schema(description = "任务名称,模糊匹配", example = "测试任务")
16 16 private String name;
17 17  
18   - @Schema(description = "任务状态,参见 JobStatusEnum 枚举", example = "1")
  18 + @Schema(description = "任务状态,参见 JobStatusEnum 枚举", example = "1")
19 19 private Integer status;
20 20  
21   - @Schema(description = "处理器的名字,模糊匹配", example = "sysUserSessionTimeoutJob")
  21 + @Schema(description = "处理器的名字,模糊匹配", example = "sysUserSessionTimeoutJob")
22 22 private String handlerName;
23 23  
24 24 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogBaseVO.java
... ... @@ -43,7 +43,7 @@ public class JobLogBaseVO {
43 43 @Schema(description = "执行时长", example = "123")
44 44 private Integer duration;
45 45  
46   - @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  46 + @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
47 47 @NotNull(message = "任务状态不能为空")
48 48 private Integer status;
49 49  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogExportReqVO.java
... ... @@ -8,14 +8,14 @@ import java.time.LocalDateTime;
8 8  
9 9 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10 10  
11   -@Schema(description = "管理后台 - 定时任务 Excel 导出 Request VO,参数和 JobLogPageReqVO 是一致的")
  11 +@Schema(description = "管理后台 - 定时任务 Excel 导出 Request VO,参数和 JobLogPageReqVO 是一致的")
12 12 @Data
13 13 public class JobLogExportReqVO {
14 14  
15 15 @Schema(description = "任务编号", example = "10")
16 16 private Long jobId;
17 17  
18   - @Schema(description = "处理器的名字,模糊匹配")
  18 + @Schema(description = "处理器的名字,模糊匹配")
19 19 private String handlerName;
20 20  
21 21 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
... ... @@ -26,7 +26,7 @@ public class JobLogExportReqVO {
26 26 @Schema(description = "结束执行时间")
27 27 private LocalDateTime endTime;
28 28  
29   - @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
  29 + @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
30 30 private Integer status;
31 31  
32 32 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogPageReqVO.java
... ... @@ -20,7 +20,7 @@ public class JobLogPageReqVO extends PageParam {
20 20 @Schema(description = "任务编号", example = "10")
21 21 private Long jobId;
22 22  
23   - @Schema(description = "处理器的名字,模糊匹配")
  23 + @Schema(description = "处理器的名字,模糊匹配")
24 24 private String handlerName;
25 25  
26 26 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
... ... @@ -31,7 +31,7 @@ public class JobLogPageReqVO extends PageParam {
31 31 @Schema(description = "结束执行时间")
32 32 private LocalDateTime endTime;
33 33  
34   - @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
  34 + @Schema(description = "任务状态,参见 JobLogStatusEnum 枚举")
35 35 private Integer status;
36 36  
37 37 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogBaseVO.java
... ... @@ -24,7 +24,7 @@ public class ApiAccessLogBaseVO {
24 24 @NotNull(message = "用户编号不能为空")
25 25 private Long userId;
26 26  
27   - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  27 + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
28 28 @NotNull(message = "用户类型不能为空")
29 29 private Integer userType;
30 30  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExportReqVO.java
... ... @@ -8,7 +8,7 @@ import java.time.LocalDateTime;
8 8  
9 9 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10 10  
11   -@Schema(description = "管理后台 - API 访问日志 Excel 导出 Request VO,参数和 ApiAccessLogPageReqVO 是一致的")
  11 +@Schema(description = "管理后台 - API 访问日志 Excel 导出 Request VO,参数和 ApiAccessLogPageReqVO 是一致的")
12 12 @Data
13 13 public class ApiAccessLogExportReqVO {
14 14  
... ... @@ -21,11 +21,11 @@ public class ApiAccessLogExportReqVO {
21 21 @Schema(description = "应用名", example = "dashboard")
22 22 private String applicationName;
23 23  
24   - @Schema(description = "请求地址,模糊匹配", example = "/xxx/yyy")
  24 + @Schema(description = "请求地址,模糊匹配", example = "/xxx/yyy")
25 25 private String requestUrl;
26 26  
  27 + @Schema(description = "开始时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
27 28 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
28   - @Schema(description = "开始请求时间")
29 29 private LocalDateTime[] beginTime;
30 30  
31 31 @Schema(description = "执行时长,大于等于,单位:毫秒", example = "100")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java
... ... @@ -26,11 +26,11 @@ public class ApiAccessLogPageReqVO extends PageParam {
26 26 @Schema(description = "应用名", example = "dashboard")
27 27 private String applicationName;
28 28  
29   - @Schema(description = "请求地址,模糊匹配", example = "/xxx/yyy")
  29 + @Schema(description = "请求地址,模糊匹配", example = "/xxx/yyy")
30 30 private String requestUrl;
31 31  
  32 + @Schema(description = "开始时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
32 33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
33   - @Schema(description = "开始请求时间")
34 34 private LocalDateTime[] beginTime;
35 35  
36 36 @Schema(description = "执行时长,大于等于,单位:毫秒", example = "100")
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExportReqVO.java
... ... @@ -8,7 +8,7 @@ import java.time.LocalDateTime;
8 8  
9 9 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10 10  
11   -@Schema(description = "管理后台 - API 错误日志 Excel 导出 Request VO,参数和 ApiErrorLogPageReqVO 是一致的")
  11 +@Schema(description = "管理后台 - API 错误日志 Excel 导出 Request VO,参数和 ApiErrorLogPageReqVO 是一致的")
12 12 @Data
13 13 public class ApiErrorLogExportReqVO {
14 14  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java
... ... @@ -7,7 +7,7 @@ import org.springframework.format.annotation.DateTimeFormat;
7 7  
8 8 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
9 9  
10   -@Schema(description = "管理后台 - 字典类型 Excel 导出 Request VO,参数和 TestDemoPageReqVO 是一致的")
  10 +@Schema(description = "管理后台 - 字典类型 Excel 导出 Request VO,参数和 TestDemoPageReqVO 是一致的")
11 11 @Data
12 12 public class TestDemoExportReqVO {
13 13  
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java
... ... @@ -57,7 +57,7 @@ public class DatabaseTableServiceImpl implements DatabaseTableService {
57 57 strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+");
58 58 }
59 59  
60   - GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 Date 类型,不使用 LocalDate
  60 + GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 LocalDateTime 类型,不使用 LocalDate
61 61 ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
62 62 null, globalConfig, null);
63 63 // 按照名字排序
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoServiceImpl.java
... ... @@ -79,7 +79,7 @@ public class TestDemoServiceImpl implements TestDemoService {
79 79  
80 80 @Override
81 81 public PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO) {
82   - testDemoMapper.selectList2();
  82 +// testDemoMapper.selectList2();
83 83 return testDemoMapper.selectPage(pageReqVO);
84 84 }
85 85  
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
... ... @@ -30,14 +30,13 @@ public interface ProductSkuApi {
30 30 */
31 31 List<ProductSkuRespDTO> getSkuList(Collection<Long> ids);
32 32  
33   - // TODO puhui999:入参用 Collection<Long> 更通用
34 33 /**
35 34 * 批量查询 SKU 数组
36 35 *
37 36 * @param spuIds SPU 编号列表
38 37 * @return SKU 数组
39 38 */
40   - List<ProductSkuRespDTO> getSkuListBySpuId(List<Long> spuIds);
  39 + List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds);
41 40  
42 41 /**
43 42 * 更新 SKU 库存
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
... ... @@ -45,10 +45,8 @@ public interface ErrorCodeConstants {
45 45 ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
46 46  
47 47 // ========== 商品 评价 1008007000 ==========
48   - ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品 评价 不存在");
49   - ErrorCode ORDER_SPU_COMMENT_EXISTS = new ErrorCode(1008007001, "订单 商品评价 已存在");
50   - ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作");
51   - ErrorCode COMMENT_ADDITIONAL_EXISTS = new ErrorCode(1008007003, "商品追加评价已存在");
  48 + ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品评价不存在");
  49 + ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1008007001, "订单的商品评价已存在");
52 50  
53 51 // ========== 商品 收藏 1008008000 ==========
54 52 ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java
... ... @@ -43,7 +43,7 @@ public class ProductSkuApiImpl implements ProductSkuApi {
43 43 }
44 44  
45 45 @Override
46   - public List<ProductSkuRespDTO> getSkuListBySpuId(List<Long> spuIds) {
  46 + public List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds) {
47 47 if (CollUtil.isEmpty(spuIds)) {
48 48 return Collections.emptyList();
49 49 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java
... ... @@ -26,23 +26,10 @@ public class ProductCommentBaseVO {
26 26 @NotNull(message = "评价人头像不能为空")
27 27 private String userAvatar;
28 28  
29   - // TODO @puhui:spuId、spuName 是不是只有 ProductCommentRespVO 有呀。
30   - @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖")
31   - @NotNull(message = "商品 SPU 编号不能为空")
32   - private Long spuId;
33   -
34   - @Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
35   - @NotNull(message = "商品 SPU 名称不能为空")
36   - private String spuName;
37   -
38 29 @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
39 30 @NotNull(message = "商品 SKU 编号不能为空")
40 31 private Long skuId;
41 32  
42   - @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
43   - @NotNull(message = "评分星级不能为空")
44   - private Integer scores;
45   -
46 33 @Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
47 34 @NotNull(message = "描述星级不能为空")
48 35 private Integer descriptionScores;
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java
... ... @@ -35,9 +35,8 @@ public class ProductCommentPageReqVO extends PageParam {
35 35 @InEnum(ProductCommentScoresEnum.class)
36 36 private Integer scores;
37 37  
38   - // TODO @puhui999:replyStatus 哈
39 38 @Schema(description = "商家是否回复", example = "true")
40   - private Boolean replied;
  39 + private Boolean replyStatus;
41 40  
42 41 @Schema(description = "创建时间")
43 42 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java
... ... @@ -5,6 +5,7 @@ import lombok.Data;
5 5 import lombok.EqualsAndHashCode;
6 6 import lombok.ToString;
7 7  
  8 +import javax.validation.constraints.NotNull;
8 9 import java.time.LocalDateTime;
9 10  
10 11 @Schema(description = "管理后台 - 商品评价 Response VO")
... ... @@ -40,4 +41,15 @@ public class ProductCommentRespVO extends ProductCommentBaseVO {
40 41 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
41 42 private LocalDateTime createTime;
42 43  
  44 + @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  45 + private Integer scores;
  46 +
  47 + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖")
  48 + @NotNull(message = "商品 SPU 编号不能为空")
  49 + private Long spuId;
  50 +
  51 + @Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  52 + @NotNull(message = "商品 SPU 名称不能为空")
  53 + private String spuName;
  54 +
43 55 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
... ... @@ -21,6 +21,7 @@ import javax.annotation.Resource;
21 21 import javax.servlet.http.HttpServletResponse;
22 22 import javax.validation.Valid;
23 23 import java.io.IOException;
  24 +import java.util.Collection;
24 25 import java.util.List;
25 26 import java.util.Map;
26 27  
... ... @@ -29,11 +30,6 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
29 30 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
30 31 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
31 32  
32   -/**
33   - * 商品 SPU 相关接口
34   - *
35   - * @author HUIHUI
36   - */
37 33 @Tag(name = "管理后台 - 商品 SPU")
38 34 @RestController
39 35 @RequestMapping("/product/spu")
... ... @@ -100,6 +96,15 @@ public class ProductSpuController {
100 96 return success(ProductSpuConvert.INSTANCE.convertList02(list));
101 97 }
102 98  
  99 + @GetMapping("/list")
  100 + @Operation(summary = "获得商品 SPU 详情列表")
  101 + @Parameter(name = "spuIds", description = "spu 编号列表", required = true, example = "[1,2,3]")
  102 + @PreAuthorize("@ss.hasPermission('product:spu:query')")
  103 + public CommonResult<List<ProductSpuDetailRespVO>> getSpuList(@RequestParam("spuIds") Collection<Long> spuIds) {
  104 + return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespListVO(
  105 + productSpuService.getSpuList(spuIds), productSkuService.getSkuListBySpuId(spuIds)));
  106 + }
  107 +
103 108 @GetMapping("/page")
104 109 @Operation(summary = "获得商品 SPU 分页")
105 110 @PreAuthorize("@ss.hasPermission('product:spu:query')")
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java
... ... @@ -9,11 +9,6 @@ import lombok.ToString;
9 9 import javax.validation.Valid;
10 10 import java.util.List;
11 11  
12   -/**
13   - * 商品 SPU 创建 Request VO
14   - *
15   - * @author HUIHUI
16   - */
17 12 @Schema(description = "管理后台 - 商品 SPU 创建 Request VO")
18 13 @Data
19 14 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java
... ... @@ -8,12 +8,6 @@ import lombok.ToString;
8 8  
9 9 import java.util.List;
10 10  
11   -/**
12   - * 商品 SPU 详细 Response VO
13   - * 包括关联的 SKU 等信息
14   - *
15   - * @author HUIHUI
16   - */
17 11 @Schema(description = "管理后台 - 商品 SPU 详细 Response VO")
18 12 @Data
19 13 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java
... ... @@ -10,12 +10,7 @@ import java.time.LocalDateTime;
10 10  
11 11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
12 12  
13   -/**
14   - * 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的
15   - *
16   - * @author HUIHUI
17   - */
18   -@Schema(description = "管理后台 - 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的")
  13 +@Schema(description = "管理后台 - 商品 SPU 导出 Request VO,参数和 ProductSpuPageReqVO 是一致的")
19 14 @Data
20 15 @NoArgsConstructor
21 16 @AllArgsConstructor
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java
... ... @@ -11,11 +11,6 @@ import java.time.LocalDateTime;
11 11  
12 12 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13 13  
14   -/**
15   - * 商品 SPU 分页 Request VO
16   - *
17   - * @author HUIHUI
18   - */
19 14 @Schema(description = "管理后台 - 商品 SPU 分页 Request VO")
20 15 @Data
21 16 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java
... ... @@ -7,11 +7,6 @@ import lombok.ToString;
7 7  
8 8 import java.time.LocalDateTime;
9 9  
10   -/**
11   - * 商品 SPU Response VO
12   - *
13   - * @author HUIHUI
14   - */
15 10 @Schema(description = "管理后台 - 商品 SPU Response VO")
16 11 @Data
17 12 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java
... ... @@ -4,11 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5 import lombok.ToString;
6 6  
7   -/**
8   - * 商品 SPU 精简 Response VO
9   - * TODO 商品 SPU 精简 VO 暂时没有使用到,用到的时候再按需添加\修改属性
10   - * @author HUIHUI
11   - */
12 7 @Schema(description = "管理后台 - 商品 SPU 精简 Response VO")
13 8 @Data
14 9 @ToString(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java
... ... @@ -12,11 +12,6 @@ import javax.validation.Valid;
12 12 import javax.validation.constraints.NotNull;
13 13 import java.util.List;
14 14  
15   -/**
16   - * 商品 SPU 更新 Request VO
17   - *
18   - * @author HUIHUI
19   - */
20 15 @Schema(description = "管理后台 - 商品 SPU 更新 Request VO")
21 16 @Data
22 17 @EqualsAndHashCode(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java
... ... @@ -7,11 +7,6 @@ import lombok.Data;
7 7  
8 8 import javax.validation.constraints.NotNull;
9 9  
10   -/**
11   - * 商品 SPU Status 更新 Request VO
12   - *
13   - * @author HUIHUI
14   - */
15 10 @Schema(description = "管理后台 - 商品 SPU Status 更新 Request VO")
16 11 @Data
17 12 public class ProductSpuUpdateStatusReqVO{
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java
1 1 package cn.iocoder.yudao.module.product.controller.app.comment;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.util.ObjectUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 7 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
8 8 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
9 9 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
10   -import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO;
11 10 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
12 11 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
13 12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
14 13 import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
15 14 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
  15 +import com.google.common.collect.Maps;
16 16 import io.swagger.v3.oas.annotations.Operation;
17 17 import io.swagger.v3.oas.annotations.Parameter;
18 18 import io.swagger.v3.oas.annotations.Parameters;
... ... @@ -26,11 +26,9 @@ import org.springframework.web.bind.annotation.RestController;
26 26  
27 27 import javax.annotation.Resource;
28 28 import javax.validation.Valid;
29   -import java.util.HashMap;
30 29 import java.util.List;
31 30 import java.util.Map;
32 31 import java.util.Set;
33   -import java.util.stream.Collectors;
34 32  
35 33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
36 34  
... ... @@ -53,34 +51,24 @@ public class AppProductCommentController {
53 51 @Parameter(name = "spuId", description = "商品 SPU 编号", required = true, example = "1024"),
54 52 @Parameter(name = "count", description = "数量", required = true, example = "10")
55 53 })
56   - public CommonResult<List<AppProductCommentRespVO>> getCommentList(@RequestParam("spuId") Long spuId,
57   - @RequestParam(value = "count", defaultValue = "10") Integer count) {
  54 + public CommonResult<List<AppProductCommentRespVO>> getCommentList(
  55 + @RequestParam("spuId") Long spuId,
  56 + @RequestParam(value = "count", defaultValue = "10") Integer count) {
58 57 return success(productCommentService.getCommentList(spuId, count));
59 58 }
60 59  
61 60 @GetMapping("/page")
62 61 @Operation(summary = "获得商品评价分页")
63 62 public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
64   - PageResult<AppProductCommentRespVO> page = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
65   - // TODO @puhui CollUtils 有简化 convertmap 和 list 的方法
66   - Set<Long> skuIds = page.getList().stream().map(AppProductCommentRespVO::getSkuId).collect(Collectors.toSet());
  63 + // TODO @puhui999:写到 convert 里,可以更简洁哈。
  64 + PageResult<ProductCommentDO> commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
  65 + Set<Long> skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId);
67 66 List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds);
68   - Map<Long, ProductSkuDO> skuDOMap = new HashMap<>(skuIds.size());
  67 + Map<Long, ProductSkuDO> skuDOMap = Maps.newLinkedHashMapWithExpectedSize(skuIds.size());
69 68 if (CollUtil.isNotEmpty(skuList)) {
70   - skuDOMap.putAll(skuList.stream().collect(Collectors.toMap(ProductSkuDO::getId, c -> c)));
  69 + skuDOMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId, c -> c));
71 70 }
72   - // TODO @puihui999:下面也可以放到 convert 里哈
73   - page.getList().forEach(item -> {
74   - // 判断用户是否选择匿名
75   - if (ObjectUtil.equal(item.getAnonymous(), true)) {
76   - item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
77   - }
78   - ProductSkuDO productSkuDO = skuDOMap.get(item.getSkuId());
79   - if (productSkuDO != null) {
80   - List<AppProductPropertyValueDetailRespVO> skuProperties = ProductCommentConvert.INSTANCE.convertList01(productSkuDO.getProperties());
81   - item.setSkuProperties(skuProperties);
82   - }
83   - });
  71 + PageResult<AppProductCommentRespVO> page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, skuDOMap);
84 72 return success(page);
85 73 }
86 74  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java
... ... @@ -10,11 +10,6 @@ import javax.validation.constraints.Size;
10 10 import java.time.LocalDateTime;
11 11 import java.util.List;
12 12  
13   -/**
14   - * 用户 App - 商品评价详情 Response VO
15   - *
16   - * @author HUIHUI
17   - */
18 13 @Schema(description = "用户 App - 商品评价详情 Response VO")
19 14 @Data
20 15 @ToString(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
1 1 package cn.iocoder.yudao.module.product.convert.comment;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
4 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
5 7 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
6 8 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
... ... @@ -19,6 +21,7 @@ import org.mapstruct.factory.Mappers;
19 21 import java.math.BigDecimal;
20 22 import java.math.RoundingMode;
21 23 import java.util.List;
  24 +import java.util.Map;
22 25  
23 26 /**
24 27 * 商品评价 Convert
... ... @@ -32,19 +35,33 @@ public interface ProductCommentConvert {
32 35  
33 36 ProductCommentRespVO convert(ProductCommentDO bean);
34 37  
35   - // TODO @puhui999:这里貌似字段对上,就不用 mapping 了;可以测试下看看哈
36   - @Mapping(target = "goodCount", source = "goodCount")
37   - @Mapping(target = "mediocreCount", source = "mediocreCount")
38   - @Mapping(target = "negativeCount", source = "negativeCount")
  38 + @Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))")
39 39 AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount);
40   -
  40 + @Named("calculateOverallScore")
  41 + default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) {
  42 + return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount);
  43 + }
  44 +
41 45 List<ProductCommentRespVO> convertList(List<ProductCommentDO> list);
42 46  
43   - List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
44   -
45 47 PageResult<ProductCommentRespVO> convertPage(PageResult<ProductCommentDO> page);
46 48  
47   - PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult);
  49 + PageResult<AppProductCommentRespVO> convertPage01(PageResult<ProductCommentDO> pageResult);
  50 +
  51 + default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult,
  52 + Map<Long, ProductSkuDO> skuMap) {
  53 + PageResult<AppProductCommentRespVO> page = convertPage01(pageResult);
  54 + page.getList().forEach(item -> {
  55 + // 判断用户是否选择匿名
  56 + if (ObjectUtil.equal(item.getAnonymous(), true)) {
  57 + item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
  58 + }
  59 + MapUtils.findAndThen(skuMap, item.getSkuId(),
  60 + sku -> item.setSkuProperties(convertList01(sku.getProperties())));
  61 + });
  62 + return page;
  63 + }
  64 + List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
48 65  
49 66 /**
50 67 * 计算综合评分
... ... @@ -63,14 +80,19 @@ public interface ProductCommentConvert {
63 80  
64 81 ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO);
65 82  
66   - @Mapping(target = "scores", expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
  83 + @Mapping(target = "scores",
  84 + expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
67 85 default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, ProductSpuDO spuDO, MemberUserRespDTO user) {
68 86 ProductCommentDO commentDO = convert(createReqDTO);
69   - commentDO.setUserId(user.getId());
70   - commentDO.setUserNickname(user.getNickname());
71   - commentDO.setUserAvatar(user.getAvatar());
72   - commentDO.setSpuId(spuDO.getId());
73   - commentDO.setSpuName(spuDO.getName());
  87 + if (user != null) {
  88 + commentDO.setUserId(user.getId());
  89 + commentDO.setUserNickname(user.getNickname());
  90 + commentDO.setUserAvatar(user.getAvatar());
  91 + }
  92 + if (spuDO != null) {
  93 + commentDO.setSpuId(spuDO.getId());
  94 + commentDO.setSpuName(spuDO.getName());
  95 + }
74 96 return commentDO;
75 97 }
76 98  
... ... @@ -78,7 +100,8 @@ public interface ProductCommentConvert {
78 100 @Mapping(target = "orderId", constant = "0L")
79 101 @Mapping(target = "orderItemId", constant = "0L")
80 102 @Mapping(target = "anonymous", expression = "java(Boolean.FALSE)")
81   - @Mapping(target = "scores", expression = "java(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()))")
  103 + @Mapping(target = "scores",
  104 + expression = "java(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()))")
82 105 ProductCommentDO convert(ProductCommentCreateReqVO createReq);
83 106  
84 107 List<AppProductCommentRespVO> convertList02(List<ProductCommentDO> list);
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
... ... @@ -18,8 +18,10 @@ import org.mapstruct.factory.Mappers;
18 18  
19 19 import java.util.ArrayList;
20 20 import java.util.List;
  21 +import java.util.Map;
21 22  
22 23 import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
23 25  
24 26 /**
25 27 * 商品 SPU Convert
... ... @@ -107,4 +109,16 @@ public interface ProductSpuConvert {
107 109 return detailRespVO;
108 110 }
109 111  
  112 + default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
  113 + List<ProductSpuDetailRespVO> vos = new ArrayList<>(spus.size());
  114 + Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);
  115 + // TODO @puhui999:可以直接使用 CollUtils.convertList
  116 + spus.forEach(spu -> {
  117 + ProductSpuDetailRespVO detailRespVO = convert03(spu);
  118 + detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())));
  119 + vos.add(detailRespVO);
  120 + });
  121 + return vos;
  122 + }
  123 +
110 124 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
... ... @@ -26,6 +26,7 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; {
26 26 }
27 27  
28 28 static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
  29 + // TODO @puhui999:是不是不用 apply 拉?直接用 mybatis 的方法就好啦
29 30 // 构建好评查询语句:好评计算 总评 >= 4
30 31 if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {
31 32 queryWrapper.apply("scores >= 4");
... ... @@ -51,11 +52,11 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; {
51 52 return selectPage(reqVO, queryWrapper);
52 53 }
53 54  
54   - default ProductCommentDO selectByUserIdAndOrderItemIdAndSpuId(Long userId, Long orderItemId, Long spuId) {
  55 + default ProductCommentDO selectByUserIdAndOrderItemIdAndSpuId(Long userId, Long orderItemId, Long skuId) {
55 56 return selectOne(new LambdaQueryWrapperX<ProductCommentDO>()
56 57 .eq(ProductCommentDO::getUserId, userId)
57 58 .eq(ProductCommentDO::getOrderItemId, orderItemId)
58   - .eq(ProductCommentDO::getSpuId, spuId));
  59 + .eq(ProductCommentDO::getSpuId, skuId));
59 60 }
60 61  
61 62 default Long selectCountBySpuId(Long spuId, Boolean visible, Integer type) {
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java
... ... @@ -54,7 +54,7 @@ public interface ProductCommentService {
54 54 * @param visible 是否可见
55 55 * @return 商品评价分页
56 56 */
57   - PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
  57 + PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible);
58 58  
59 59 /**
60 60 * 创建商品评论
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java
... ... @@ -81,7 +81,7 @@ public class ProductCommentServiceImpl implements ProductCommentService {
81 81 @Transactional(rollbackFor = Exception.class)
82 82 public void createComment(ProductCommentCreateReqVO createReqVO) {
83 83 // 校验评论
84   - validateComment(createReqVO.getSpuId(), createReqVO.getUserId(), createReqVO.getOrderItemId());
  84 + validateComment(createReqVO.getSkuId(), createReqVO.getUserId(), createReqVO.getOrderItemId());
85 85  
86 86 ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqVO);
87 87 productCommentMapper.insert(commentDO);
... ... @@ -108,11 +108,11 @@ public class ProductCommentServiceImpl implements ProductCommentService {
108 108 return commentDO.getId();
109 109 }
110 110  
111   - private void validateComment(Long spuId, Long userId, Long orderItemId) {
  111 + private void validateComment(Long skuId, Long userId, Long orderItemId) {
112 112 // 判断当前订单的当前商品用户是否评价过
113   - ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, spuId);
  113 + ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, skuId);
114 114 if (null != exist) {
115   - throw exception(ORDER_SPU_COMMENT_EXISTS);
  115 + throw exception(COMMENT_ORDER_EXISTS);
116 116 }
117 117 }
118 118  
... ... @@ -141,23 +141,17 @@ public class ProductCommentServiceImpl implements ProductCommentService {
141 141 productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.MEDIOCRE_COMMENT),
142 142 // 查询商品 id = spuId 的所有差评数量
143 143 productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.NEGATIVE_COMMENT)
144   - ).setScores(3.0); // TODO @puhui999:这里要实现下;;
  144 + );
145 145 }
146 146  
147 147 @Override
148 148 public List<AppProductCommentRespVO> getCommentList(Long spuId, Integer count) {
149   - // 校验商品 spu 是否存在
150   - // TODO @puhui 这里校验可以去掉哈。
151   - ProductSpuDO spuDO = validateSpu(spuId);
152   - return ProductCommentConvert.INSTANCE.convertList02(productCommentMapper.selectCommentList(spuDO.getId(), count).getList());
  149 + return ProductCommentConvert.INSTANCE.convertList02(productCommentMapper.selectCommentList(spuId, count).getList());
153 150 }
154 151  
155   - // TODO @puhui 可以放到 controller 去 convert 哈
156 152 @Override
157   - public PageResult<AppProductCommentRespVO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
158   - // TODO @puhui 可以放到 controller 去 convert 哈
159   - return ProductCommentConvert.INSTANCE.convertPage02(
160   - productCommentMapper.selectPage(pageVO, visible));
  153 + public PageResult<ProductCommentDO> getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) {
  154 + return productCommentMapper.selectPage(pageVO, visible);
161 155 }
162 156  
163 157 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
... ... @@ -90,7 +90,7 @@ public interface ProductSkuService {
90 90 * @param spuIds spu 编码集合
91 91 * @return 商品 sku 集合
92 92 */
93   - List<ProductSkuDO> getSkuListBySpuId(List<Long> spuIds);
  93 + List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds);
94 94  
95 95 /**
96 96 * 通过 spuId 删除 sku 信息
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
... ... @@ -148,7 +148,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
148 148 }
149 149  
150 150 @Override
151   - public List<ProductSkuDO> getSkuListBySpuId(List<Long> spuIds) {
  151 + public List<ProductSkuDO> getSkuListBySpuId(Collection<Long> spuIds) {
152 152 return productSkuMapper.selectListBySpuId(spuIds);
153 153 }
154 154  
... ...
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java
... ... @@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen
10 10 import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO;
11 11 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO;
12 12 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO;
13   -import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
14 13 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
15 14 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
16 15 import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
... ... @@ -128,7 +127,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
128 127 productCommentPageReqVO.setSpuId(spuId);
129 128 productCommentPageReqVO.setSpuName("感冒药");
130 129 productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores());
131   - productCommentPageReqVO.setReplied(Boolean.TRUE);
  130 + productCommentPageReqVO.setReplyStatus(Boolean.TRUE);
132 131  
133 132 PageResult<ProductCommentDO> commentPage = productCommentService.getCommentPage(productCommentPageReqVO);
134 133 PageResult<ProductCommentRespVO> result = ProductCommentConvert.INSTANCE.convertPage(productCommentMapper.selectPage(productCommentPageReqVO));
... ... @@ -138,15 +137,15 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
138 137 assertEquals(8, all.getTotal());
139 138  
140 139 // 测试获取所有商品分页评论数据
141   - PageResult<AppProductCommentRespVO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);
  140 + PageResult<ProductCommentDO> result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE);
142 141 assertEquals(7, result1.getTotal());
143 142  
144 143 // 测试获取所有商品分页中评数据
145   - PageResult<AppProductCommentRespVO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
  144 + PageResult<ProductCommentDO> result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
146 145 assertEquals(2, result2.getTotal());
147 146  
148 147 // 测试获取指定 spuId 商品分页中评数据
149   - PageResult<AppProductCommentRespVO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
  148 + PageResult<ProductCommentDO> result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE);
150 149 assertEquals(2, result3.getTotal());
151 150  
152 151 // 测试分页 tab count
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  4 +
  5 +import javax.validation.Valid;
  6 +
  7 +// TODO @puhui999:CombinationRecordApi 分成活动、记录哈
  8 +// TODO @芋艿:后面也再撸撸这几个接口
  9 +/**
  10 + * 拼团活动 API 接口
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +public interface CombinationApi {
  15 +
  16 + /**
  17 + * 创建开团记录
  18 + *
  19 + * @param reqDTO 请求 DTO
  20 + */
  21 + void createRecord(@Valid CombinationRecordReqDTO reqDTO);
  22 +
  23 + /**
  24 + * 获取开团记录状态
  25 + *
  26 + * @param userId 用户编号
  27 + * @param orderId 订单编号
  28 + */
  29 + boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
  30 +
  31 + /**
  32 + * 更新开团记录状态
  33 + *
  34 + * @param userId 用户编号
  35 + * @param orderId 订单编号
  36 + * @param status 状态值
  37 + */
  38 + void updateRecordStatus(Long userId, Long orderId, Integer status);
  39 +
  40 + /**
  41 + * 更新开团记录状态和开始时间
  42 + *
  43 + * @param userId 用户编号
  44 + * @param orderId 订单编号
  45 + * @param status 状态值
  46 + */
  47 + void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status);
  48 +
  49 +}
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.validation.constraints.NotEmpty;
  6 +import javax.validation.constraints.NotNull;
  7 +
  8 +// TODO @puhui999:CombinationRecordCreateReqDTO,这样更容易知道是创建噢
  9 +/**
  10 + * 拼团记录 Request DTO
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Data
  15 +public class CombinationRecordReqDTO {
  16 +
  17 + /**
  18 + * 拼团活动编号
  19 + */
  20 + @NotNull(message = "拼团活动编号不能为空")
  21 + private Long activityId;
  22 + /**
  23 + * spu 编号
  24 + */
  25 + @NotNull(message = "spu 编号不能为空")
  26 + private Long spuId;
  27 + /**
  28 + * sku 编号
  29 + */
  30 + @NotNull(message = "sku 编号不能为空")
  31 + private Long skuId;
  32 + /**
  33 + * 用户编号
  34 + */
  35 + @NotNull(message = "用户编号不能为空")
  36 + private Long userId;
  37 + /**
  38 + * 订单编号
  39 + */
  40 + @NotNull(message = "订单编号不能为空")
  41 + private Long orderId;
  42 + /**
  43 + * 团长编号
  44 + */
  45 + @NotNull(message = "团长编号不能为空")
  46 + private Long headId;
  47 + /**
  48 + * 商品名字
  49 + */
  50 + @NotEmpty(message = "商品名字不能为空")
  51 + private String spuName;
  52 + /**
  53 + * 商品图片
  54 + */
  55 + @NotEmpty(message = "商品图片不能为空")
  56 + private String picUrl;
  57 + /**
  58 + * 拼团商品单价
  59 + */
  60 + @NotNull(message = "拼团商品单价不能为空")
  61 + private Integer combinationPrice;
  62 + /**
  63 + * 用户昵称
  64 + */
  65 + @NotEmpty(message = "用户昵称不能为空")
  66 + private String nickname;
  67 + /**
  68 + * 用户头像
  69 + */
  70 + @NotEmpty(message = "用户头像不能为空")
  71 + private String avatar;
  72 + /**
  73 + * 开团状态:正在开团 拼团成功 拼团失败 TODO 等待支付
  74 + */
  75 + @NotNull(message = "开团状态不能为空")
  76 + private Integer status;
  77 +
  78 +}
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4  
5 5 /**
6 6 * Promotion 错误码枚举类
7   - *
  7 + * <p>
8 8 * promotion 系统,使用 1-013-000-000 段
9 9 */
10 10 public interface ErrorCodeConstants {
... ... @@ -42,8 +42,7 @@ public interface ErrorCodeConstants {
42 42 ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭");
43 43 ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭");
44 44  
45   - // ========== Price 相关 1013007000 ============
46   - ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1013007000, "支付价格计算异常,原因:价格小于等于 0");
  45 + // ========== TODO 空着 1013007000 ============
47 46  
48 47 // ========== 秒杀活动 1013008000 ==========
49 48 ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
... ... @@ -58,5 +57,13 @@ public interface ErrorCodeConstants {
58 57 ErrorCode SECKILL_TIME_CONFLICTS = new ErrorCode(1013009001, "秒杀时段冲突");
59 58 ErrorCode SECKILL_TIME_EQUAL = new ErrorCode(1013009002, "秒杀时段开始时间和结束时间不能相等");
60 59 ErrorCode SECKILL_START_TIME_BEFORE_END_TIME = new ErrorCode(1013009003, "秒杀时段开始时间不能在结束时间之后");
  60 + ErrorCode SECKILL_TIME_DISABLE = new ErrorCode(1013009004, "秒杀时段已关闭");
  61 +
  62 + // ========== 拼团活动 1013010000 ==========
  63 + ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在");
  64 + ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
  65 + ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
  66 + ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
  67 + ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
61 68  
62 69 }
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.enums.combination;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * 拼团状态枚举
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum CombinationRecordStatusEnum implements IntArrayValuable {
  17 +
  18 + WAITING(0, "未付款"),
  19 + IN_PROGRESS(1, "进行中"),
  20 + SUCCESS(2, "拼团成功"),
  21 + FAILED(3, "拼团失败");
  22 +
  23 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CombinationRecordStatusEnum::getStatus).toArray();
  24 +
  25 + /**
  26 + * 状态值
  27 + */
  28 + private final Integer status;
  29 + /**
  30 + * 状态名
  31 + */
  32 + private final String name;
  33 +
  34 + @Override
  35 + public int[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  4 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  5 +import org.springframework.stereotype.Service;
  6 +
  7 +import javax.annotation.Resource;
  8 +import java.time.LocalDateTime;
  9 +
  10 +/**
  11 + * 拼团活动 API 实现类
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@Service
  16 +public class CombinationApiImpl implements CombinationApi {
  17 +
  18 + @Resource
  19 + private CombinationActivityService activityService;
  20 +
  21 + @Override
  22 + public void createRecord(CombinationRecordReqDTO reqDTO) {
  23 + activityService.createRecord(reqDTO);
  24 + }
  25 +
  26 + @Override
  27 + public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
  28 + return activityService.validateRecordStatusIsSuccess(userId, orderId);
  29 + }
  30 +
  31 + @Override
  32 + public void updateRecordStatus(Long userId, Long orderId, Integer status) {
  33 + activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
  34 + }
  35 +
  36 + @Override
  37 + public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) {
  38 + activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
  39 + }
  40 +
  41 +
  42 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  7 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  8 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  9 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  10 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;
  11 +import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
  13 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  14 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  15 +import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import org.springframework.security.access.prepost.PreAuthorize;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
  21 +
  22 +import javax.annotation.Resource;
  23 +import javax.servlet.http.HttpServletResponse;
  24 +import javax.validation.Valid;
  25 +import java.io.IOException;
  26 +import java.util.Collection;
  27 +import java.util.List;
  28 +import java.util.Set;
  29 +
  30 +import static cn.hutool.core.collection.CollectionUtil.newArrayList;
  31 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  32 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  33 +
  34 +@Tag(name = "管理后台 - 拼团活动")
  35 +@RestController
  36 +@RequestMapping("/promotion/combination-activity")
  37 +@Validated
  38 +public class CombinationActivityController {
  39 +
  40 + @Resource
  41 + private CombinationActivityService combinationActivityService;
  42 + @Resource
  43 + private ProductSpuApi spuApi;
  44 +
  45 + @PostMapping("/create")
  46 + @Operation(summary = "创建拼团活动")
  47 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:create')")
  48 + public CommonResult<Long> createCombinationActivity(@Valid @RequestBody CombinationActivityCreateReqVO createReqVO) {
  49 + return success(combinationActivityService.createCombinationActivity(createReqVO));
  50 + }
  51 +
  52 + @PutMapping("/update")
  53 + @Operation(summary = "更新拼团活动")
  54 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:update')")
  55 + public CommonResult<Boolean> updateCombinationActivity(@Valid @RequestBody CombinationActivityUpdateReqVO updateReqVO) {
  56 + combinationActivityService.updateCombinationActivity(updateReqVO);
  57 + return success(true);
  58 + }
  59 +
  60 + @DeleteMapping("/delete")
  61 + @Operation(summary = "删除拼团活动")
  62 + @Parameter(name = "id", description = "编号", required = true)
  63 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:delete')")
  64 + public CommonResult<Boolean> deleteCombinationActivity(@RequestParam("id") Long id) {
  65 + combinationActivityService.deleteCombinationActivity(id);
  66 + return success(true);
  67 + }
  68 +
  69 + @GetMapping("/get")
  70 + @Operation(summary = "获得拼团活动")
  71 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  72 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
  73 + public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) {
  74 + CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);
  75 + List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(newArrayList(id));
  76 + return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
  77 + }
  78 +
  79 + @GetMapping("/list")
  80 + @Operation(summary = "获得拼团活动列表")
  81 + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
  82 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
  83 + public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityList(@RequestParam("ids") Collection<Long> ids) {
  84 + List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(ids);
  85 + return success(CombinationActivityConvert.INSTANCE.convertList(list));
  86 + }
  87 +
  88 + @GetMapping("/page")
  89 + @Operation(summary = "获得拼团活动分页")
  90 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
  91 + public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(
  92 + @Valid CombinationActivityPageReqVO pageVO) {
  93 + PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
  94 + // TODO @puhui999:可以不一定 aIds,直接批量查询结果出来;下面也是类似;
  95 + Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId);
  96 + List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(aIds);
  97 + Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId);
  98 + List<ProductSpuRespDTO> spus = spuApi.getSpuList(spuIds);
  99 + return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
  100 + }
  101 +
  102 + @GetMapping("/export-excel")
  103 + @Operation(summary = "导出拼团活动 Excel")
  104 + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:export')")
  105 + @OperateLog(type = EXPORT)
  106 + public void exportCombinationActivityExcel(@Valid CombinationActivityExportReqVO exportReqVO,
  107 + HttpServletResponse response) throws IOException {
  108 + List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(exportReqVO);
  109 + // 导出 Excel
  110 + List<CombinationActivityExcelVO> datas = CombinationActivityConvert.INSTANCE.convertList02(list);
  111 + ExcelUtils.write(response, "拼团活动.xls", "数据", CombinationActivityExcelVO.class, datas);
  112 + }
  113 +
  114 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import org.springframework.format.annotation.DateTimeFormat;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +/**
  13 + * 拼团活动 Base VO,提供给添加、修改、详细的子 VO 使用
  14 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  15 + */
  16 +@Data
  17 +public class CombinationActivityBaseVO {
  18 +
  19 + @Schema(description = "拼团名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "越拼越省钱")
  20 + @NotNull(message = "拼团名称不能为空")
  21 + private String name;
  22 +
  23 + @Schema(description = "商品 SPU 编号,关联 ProductSpuDO 的 id", example = "[1,2,3]")
  24 + @NotNull(message = "拼团商品不能为空")
  25 + private Long spuId;
  26 +
  27 + @Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218")
  28 + @NotNull(message = "总限购数量不能为空")
  29 + private Integer totalLimitCount;
  30 +
  31 + @Schema(description = "单次限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "28265")
  32 + @NotNull(message = "单次限购数量不能为空")
  33 + private Integer singleLimitCount;
  34 +
  35 + // TODO @puhui999:是不是弄成 2 个字段会好点哈。开始、结束
  36 + @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
  37 + @NotNull(message = "活动时间不能为空")
  38 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  39 + private LocalDateTime[] activityTime;
  40 +
  41 + @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
  42 + @NotNull(message = "开团人数不能为空")
  43 + private Integer userSize;
  44 +
  45 + @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + @NotNull(message = "限制时长不能为空")
  47 + private Integer limitDuration;
  48 +
  49 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - 拼团活动创建 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO {
  17 +
  18 + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
  19 + @Valid
  20 + private List<CombinationProductCreateReqVO> products;
  21 +
  22 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import com.alibaba.excel.annotation.ExcelProperty;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +// TODO @puhui999:如无必要,导出都可以删除哈
  11 +/**
  12 + * 拼团活动 Excel VO
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Data
  17 +public class CombinationActivityExcelVO {
  18 +
  19 + @ExcelProperty("活动编号")
  20 + private Long id;
  21 +
  22 + @ExcelProperty("拼团名称")
  23 + private String name;
  24 +
  25 + @ExcelProperty("商品 SPU 编号关联 ProductSpuDO 的 id")
  26 + private Long spuId;
  27 +
  28 + @ExcelProperty("总限购数量")
  29 + private Integer totalLimitCount;
  30 +
  31 + @ExcelProperty("单次限购数量")
  32 + private Integer singleLimitCount;
  33 +
  34 + @ExcelProperty("开始时间")
  35 + private LocalDateTime startTime;
  36 +
  37 + @ExcelProperty("结束时间")
  38 + private LocalDateTime endTime;
  39 +
  40 + @ExcelProperty("开团人数")
  41 + private Integer userSize;
  42 +
  43 + @ExcelProperty("开团组数")
  44 + private Integer totalNum;
  45 +
  46 + @ExcelProperty("成团组数")
  47 + private Integer successNum;
  48 +
  49 + @ExcelProperty("参与人数")
  50 + private Integer orderUserCount;
  51 +
  52 + @ExcelProperty("虚拟成团")
  53 + private Integer virtualGroup;
  54 +
  55 + @ExcelProperty(value = "活动状态:0开启 1关闭", converter = DictConvert.class)
  56 + @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
  57 + private Integer status;
  58 +
  59 + @ExcelProperty("限制时长(小时)")
  60 + private Integer limitDuration;
  61 +
  62 + @ExcelProperty("创建时间")
  63 + private LocalDateTime createTime;
  64 +
  65 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import org.springframework.format.annotation.DateTimeFormat;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +// TODO @puhui999:如无必要,导出都可以删除哈
  12 +@Schema(description = "管理后台 - 拼团活动 Excel 导出 Request VO,参数和 CombinationActivityPageReqVO 是一致的")
  13 +@Data
  14 +public class CombinationActivityExportReqVO {
  15 +
  16 + @Schema(description = "拼团名称", example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "14016")
  20 + private Long spuId;
  21 +
  22 + @Schema(description = "总限购数量", example = "16218")
  23 + private Integer totalLimitCount;
  24 +
  25 + @Schema(description = "单次限购数量", example = "28265")
  26 + private Integer singleLimitCount;
  27 +
  28 + @Schema(description = "开始时间")
  29 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  30 + private LocalDateTime[] startTime;
  31 +
  32 + @Schema(description = "结束时间")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] endTime;
  35 +
  36 + @Schema(description = "开团人数")
  37 + private Integer userSize;
  38 +
  39 + @Schema(description = "开团组数")
  40 + private Integer totalNum;
  41 +
  42 + @Schema(description = "成团组数")
  43 + private Integer successNum;
  44 +
  45 + @Schema(description = "参与人数", example = "25222")
  46 + private Integer orderUserCount;
  47 +
  48 + @Schema(description = "虚拟成团")
  49 + private Integer virtualGroup;
  50 +
  51 + @Schema(description = "活动状态:0开启 1关闭", example = "0")
  52 + private Integer status;
  53 +
  54 + @Schema(description = "限制时长(小时)")
  55 + private Integer limitDuration;
  56 +
  57 + @Schema(description = "创建时间")
  58 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  59 + private LocalDateTime[] createTime;
  60 +
  61 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 拼团活动分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class CombinationActivityPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "拼团名称", example = "赵六")
  21 + private String name;
  22 +
  23 + @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "14016")
  24 + private Long spuId;
  25 +
  26 + @Schema(description = "总限购数量", example = "16218")
  27 + private Integer totalLimitCount;
  28 +
  29 + @Schema(description = "单次限购数量", example = "28265")
  30 + private Integer singleLimitCount;
  31 +
  32 + @Schema(description = "开始时间")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] startTime;
  35 +
  36 + @Schema(description = "结束时间")
  37 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  38 + private LocalDateTime[] endTime;
  39 +
  40 + @Schema(description = "开团人数")
  41 + private Integer userSize;
  42 +
  43 + @Schema(description = "开团组数")
  44 + private Integer totalNum;
  45 +
  46 + @Schema(description = "成团组数")
  47 + private Integer successNum;
  48 +
  49 + @Schema(description = "参与人数", example = "25222")
  50 + private Integer orderUserCount;
  51 +
  52 + @Schema(description = "虚拟成团")
  53 + private Integer virtualGroup;
  54 +
  55 + @Schema(description = "活动状态:0开启 1关闭", example = "0")
  56 + private Integer status;
  57 +
  58 + @Schema(description = "限制时长(小时)")
  59 + private Integer limitDuration;
  60 +
  61 + @Schema(description = "创建时间")
  62 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  63 + private LocalDateTime[] createTime;
  64 +
  65 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.Valid;
  10 +import javax.validation.constraints.NotNull;
  11 +import java.time.LocalDateTime;
  12 +import java.util.List;
  13 +
  14 +@Schema(description = "管理后台 - 拼团活动 Response VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class CombinationActivityRespVO extends CombinationActivityBaseVO {
  19 +
  20 + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
  21 + private String spuName;
  22 +
  23 + @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
  24 + private String picUrl;
  25 +
  26 + @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
  27 + private Long id;
  28 +
  29 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + private LocalDateTime createTime;
  31 +
  32 + @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + @NotNull(message = "开团人数不能为空")
  34 + private Integer userSize;
  35 +
  36 + @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + @NotNull(message = "开团组数不能为空")
  38 + private Integer totalNum;
  39 +
  40 + @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED)
  41 + @NotNull(message = "成团组数不能为空")
  42 + private Integer successNum;
  43 +
  44 + @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED)
  45 + @NotNull(message = "虚拟成团不能为空")
  46 + private Integer virtualGroup;
  47 +
  48 + @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  49 + @NotNull(message = "活动状态不能为空")
  50 + private Integer status;
  51 +
  52 + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
  53 + @Valid
  54 + private List<CombinationProductRespVO> products;
  55 +
  56 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.Valid;
  10 +import javax.validation.constraints.NotNull;
  11 +import java.util.List;
  12 +
  13 +@Schema(description = "管理后台 - 拼团活动更新 Request VO")
  14 +@Data
  15 +@EqualsAndHashCode(callSuper = true)
  16 +@ToString(callSuper = true)
  17 +public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO {
  18 +
  19 + @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
  20 + @NotNull(message = "活动编号不能为空")
  21 + private Long id;
  22 +
  23 + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + @Valid
  25 + private List<CombinationProductUpdateReqVO> products;
  26 +
  27 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.constraints.NotNull;
  7 +
  8 +/**
  9 + * 拼团商品 Base VO,提供给添加、修改、详细的子 VO 使用
  10 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  11 + */
  12 +@Data
  13 +public class CombinationProductBaseVO {
  14 +
  15 + @Schema(description = "商品 spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
  16 + @NotNull(message = "商品 spuId 不能为空")
  17 + private Long spuId;
  18 +
  19 + @Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
  20 + @NotNull(message = "商品 skuId 不能为空")
  21 + private Long skuId;
  22 +
  23 + @Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682")
  24 + @NotNull(message = "拼团价格,单位分不能为空")
  25 + private Integer activePrice;
  26 +
  27 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +@Schema(description = "管理后台 - 拼团商品创建 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class CombinationProductCreateReqVO extends CombinationProductBaseVO {
  13 +
  14 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExcelVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelProperty;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +// TODO @puhui999:可以考虑删除 excel 导出哈
  9 +/**
  10 + * 拼团商品 Excel VO
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Data
  15 +public class CombinationProductExcelVO {
  16 +
  17 + @ExcelProperty("编号")
  18 + private Long id;
  19 +
  20 + @ExcelProperty("拼团活动编号")
  21 + private Long activityId;
  22 +
  23 + @ExcelProperty("商品 SPU 编号")
  24 + private Long spuId;
  25 +
  26 + @ExcelProperty("商品 SKU 编号")
  27 + private Long skuId;
  28 +
  29 + @ExcelProperty("拼团商品状态")
  30 + private Integer activityStatus;
  31 +
  32 + @ExcelProperty("活动开始时间点")
  33 + private LocalDateTime activityStartTime;
  34 +
  35 + @ExcelProperty("活动结束时间点")
  36 + private LocalDateTime activityEndTime;
  37 +
  38 + @ExcelProperty("拼团价格,单位分")
  39 + private Integer activePrice;
  40 +
  41 + @ExcelProperty("创建时间")
  42 + private LocalDateTime createTime;
  43 +
  44 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExportReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import org.springframework.format.annotation.DateTimeFormat;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +// TODO @puhui999:可以考虑删除 excel 导出哈
  12 +@Schema(description = "管理后台 - 拼团商品 Excel 导出 Request VO,参数和 CombinationProductPageReqVO 是一致的")
  13 +@Data
  14 +public class CombinationProductExportReqVO {
  15 +
  16 + @Schema(description = "拼团活动编号", example = "6829")
  17 + private Long activityId;
  18 +
  19 + @Schema(description = "商品 SPU 编号", example = "18731")
  20 + private Long spuId;
  21 +
  22 + @Schema(description = "商品 SKU 编号", example = "31675")
  23 + private Long skuId;
  24 +
  25 + @Schema(description = "拼团商品状态", example = "2")
  26 + private Integer activityStatus;
  27 +
  28 + @Schema(description = "活动开始时间点")
  29 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  30 + private LocalDateTime[] activityStartTime;
  31 +
  32 + @Schema(description = "活动结束时间点")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] activityEndTime;
  35 +
  36 + @Schema(description = "拼团价格,单位分", example = "27682")
  37 + private Integer activePrice;
  38 +
  39 + @Schema(description = "创建时间")
  40 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  41 + private LocalDateTime[] createTime;
  42 +
  43 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 拼团商品分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class CombinationProductPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "拼团活动编号", example = "6829")
  21 + private Long activityId;
  22 +
  23 + @Schema(description = "商品 SPU 编号", example = "18731")
  24 + private Long spuId;
  25 +
  26 + @Schema(description = "商品 SKU 编号", example = "31675")
  27 + private Long skuId;
  28 +
  29 + @Schema(description = "拼团商品状态", example = "2")
  30 + private Integer activityStatus;
  31 +
  32 + @Schema(description = "活动开始时间点")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] activityStartTime;
  35 +
  36 + @Schema(description = "活动结束时间点")
  37 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  38 + private LocalDateTime[] activityEndTime;
  39 +
  40 + @Schema(description = "拼团价格,单位分", example = "27682")
  41 + private Integer activePrice;
  42 +
  43 + @Schema(description = "创建时间")
  44 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  45 + private LocalDateTime[] createTime;
  46 +
  47 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 拼团商品 Response VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class CombinationProductRespVO extends CombinationProductBaseVO {
  15 +
  16 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28322")
  17 + private Long id;
  18 +
  19 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + private LocalDateTime createTime;
  21 +
  22 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +@Schema(description = "管理后台 - 拼团商品更新 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class CombinationProductUpdateReqVO extends CombinationProductBaseVO {
  13 +
  14 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java
... ... @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  7 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
5 8 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.*;
6 9 import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
7 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
... ... @@ -18,6 +21,7 @@ import javax.annotation.Resource;
18 21 import javax.validation.Valid;
19 22 import java.util.Collection;
20 23 import java.util.List;
  24 +import java.util.Set;
21 25  
22 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23 27  
... ... @@ -29,6 +33,8 @@ public class SeckillActivityController {
29 33  
30 34 @Resource
31 35 private SeckillActivityService seckillActivityService;
  36 + @Resource
  37 + private ProductSpuApi spuApi;
32 38  
33 39 @PostMapping("/create")
34 40 @Operation(summary = "创建秒杀活动")
... ... @@ -69,11 +75,8 @@ public class SeckillActivityController {
69 75 @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
70 76 public CommonResult<SeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
71 77 SeckillActivityDO seckillActivity = seckillActivityService.getSeckillActivity(id);
72   - if (seckillActivity == null) {
73   - return success(null);
74   - }
75   - List<SeckillProductDO> seckillProducts = seckillActivityService.getSeckillProductListByActivityId(id);
76   - return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity,seckillProducts));
  78 + List<SeckillProductDO> seckillProducts = seckillActivityService.getSeckillProductListByActivityId(id);
  79 + return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity, seckillProducts));
77 80 }
78 81  
79 82 @GetMapping("/list")
... ... @@ -90,7 +93,11 @@ public class SeckillActivityController {
90 93 @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
91 94 public CommonResult<PageResult<SeckillActivityRespVO>> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) {
92 95 PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(pageVO);
93   - return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult));
  96 + Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getId);
  97 + List<SeckillProductDO> seckillProducts = seckillActivityService.getSeckillProductListByActivityId(aIds);
  98 + Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getSpuId);
  99 + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(spuIds);
  100 + return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, seckillProducts, spuList));
94 101 }
95 102  
96 103 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
... ... @@ -19,11 +19,6 @@ import java.util.List;
19 19  
20 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 21  
22   -/**
23   - * 管理后台 - 秒杀时段相关接口
24   - *
25   - * @author HUIHUI
26   - */
27 22 @Tag(name = "管理后台 - 秒杀时段")
28 23 @RestController
29 24 @RequestMapping("/promotion/seckill-config")
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java
... ... @@ -20,10 +20,9 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
20 20 @Data
21 21 public class SeckillActivityBaseVO {
22 22  
23   - // TODO @puhui999:对应单 spuId 哈
24 23 @Schema(description = "秒杀活动商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[121,1212]")
25 24 @NotNull(message = "秒杀活动商品不能为空")
26   - private List<Long> spuIds;
  25 + private Long spuId;
27 26  
28 27 @Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
29 28 @NotNull(message = "秒杀活动名称不能为空")
... ... @@ -56,8 +55,4 @@ public class SeckillActivityBaseVO {
56 55 @Schema(description = "单次限够数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "31683")
57 56 private Integer singleLimitCount;
58 57  
59   - // TODO @puhui999:这个应该是计算出来的字段,只返回,create 和 update 不用哈
60   - @Schema(description = "秒杀总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
61   - private Integer totalStock;
62   -
63 58 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java
... ... @@ -6,26 +6,46 @@ import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
7 7 import lombok.ToString;
8 8  
  9 +import java.time.LocalDateTime;
9 10 import java.util.List;
10 11  
11   -/**
12   - * 管理后台 - 秒杀活动 Response VO
13   - *
14   - * @author HUIHUI
15   - */
16 12 @Schema(description = "管理后台 - 秒杀活动 Response VO")
17 13 @Data
18 14 @EqualsAndHashCode(callSuper = true)
19 15 @ToString(callSuper = true)
20 16 public class SeckillActivityRespVO extends SeckillActivityBaseVO {
21 17  
  18 + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
  19 + private String spuName;
  20 +
  21 + @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
  22 + private String picUrl;
  23 +
22 24 @Schema(description = "秒杀活动id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
23 25 private Long id;
24 26  
25 27 @Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
26   - private List<SeckillProductRespVO> products; // TODO puhui: 考虑是否去除
  28 + private List<SeckillProductRespVO> products;
27 29  
28 30 @Schema(description = "活动状态 开启:0 禁用:1", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
29 31 private Integer status;
30 32  
  33 + @Schema(description = "订单实付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "22354")
  34 + private Integer totalPrice;
  35 +
  36 + @Schema(description = "秒杀库存", example = "10")
  37 + private Integer stock;
  38 +
  39 + @Schema(description = "秒杀总库存", example = "20")
  40 + private Integer totalStock;
  41 +
  42 + @Schema(description = "新增订单数", example = "20")
  43 + private Integer orderCount;
  44 +
  45 + @Schema(description = "付款人数", example = "20")
  46 + private Integer userCount;
  47 +
  48 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  49 + private LocalDateTime createTime;
  50 +
31 51 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java
1 1 package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;
2 2  
  3 +import com.fasterxml.jackson.annotation.JsonIgnore;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5 import lombok.Data;
5 6  
  7 +import javax.validation.constraints.AssertTrue;
6 8 import javax.validation.constraints.NotNull;
  9 +import java.time.LocalTime;
  10 +import java.util.List;
7 11  
8 12 /**
9 13 * 秒杀时段 Base VO,提供给添加、修改、详细的子 VO 使用
... ... @@ -26,12 +30,24 @@ public class SeckillConfigBaseVO {
26 30 @NotNull(message = "结束时间点不能为空")
27 31 private String endTime;
28 32  
29   - @Schema(description = "秒杀主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
30   - @NotNull(message = "秒杀主图不能为空")
31   - private String picUrl;
  33 + @Schema(description = "秒杀轮播图", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]")
  34 + @NotNull(message = "秒杀轮播图不能为空")
  35 + private List<String> sliderPicUrls;
32 36  
33 37 @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
34 38 @NotNull(message = "状态不能为空")
35 39 private Integer status;
36 40  
  41 + @AssertTrue(message = "秒杀时段开始时间和结束时间不能相等")
  42 + @JsonIgnore
  43 + public boolean isValidStartTimeValid() {
  44 + return !LocalTime.parse(startTime).equals(LocalTime.parse(endTime));
  45 + }
  46 +
  47 + @AssertTrue(message = "秒杀时段开始时间不能在结束时间之后")
  48 + @JsonIgnore
  49 + public boolean isValidEndTimeValid() {
  50 + return !LocalTime.parse(startTime).isAfter(LocalTime.parse(endTime));
  51 + }
  52 +
37 53 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java
... ... @@ -6,12 +6,6 @@ import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
7 7 import lombok.ToString;
8 8  
9   -// TODO @puhui:VO 上不写注释,已经有注解啦。
10   -/**
11   - * 管理后台 - 秒杀时段分页 Request VO
12   - *
13   - * @author HUIHUI
14   - */
15 9 @Schema(description = "管理后台 - 秒杀时段分页 Request VO")
16 10 @Data
17 11 @EqualsAndHashCode(callSuper = true)
... ...