Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product
# Conflicts: # yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
This commit is contained in:
commit
57d782bc94
58
README.md
58
README.md
|
@ -15,6 +15,8 @@
|
|||
* 启动文档:<https://doc.iocoder.cn/quick-start/>
|
||||
* 视频教程:<https://doc.iocoder.cn/video/>
|
||||
|
||||
已支持 Spring Boot 3.X + JDK 17 版本,可见 [master-boot3](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/README.md) 分支。
|
||||
|
||||
## 🐯 平台简介
|
||||
|
||||
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
|
||||
|
@ -62,18 +64,18 @@
|
|||
| [yudao-ui-admin](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-admin) | 基于 Vue2 + element-ui 实现的管理后台 |
|
||||
| [yudao-ui-admin-uniapp](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) | 基于 uni-app + uni-ui 实现的管理后台的小程序 |
|
||||
| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 |
|
||||
| [yudao-ui-app](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-app) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-app) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-app) | 基于 uni-app + uview 实现的用户 App |
|
||||
| [yudao-mall-uniapp](https://gitee.com/yudaocode/yudao-mall-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-mall-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-mall-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-mall-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-mall-uniapp) | 基于 uni-app 实现的商城小程序 |
|
||||
|
||||
## 🐰 分支说明
|
||||
|
||||
| | JDK 8 完整版 | JDK 8 精简版 | JDK 17 完整版 |
|
||||
|-------|-----------------------------------------------------------|--------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| 分支 | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`boot-dev`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/boot3-dev/) |
|
||||
| 分支 | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`master-boot3`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-boot3/) |
|
||||
| 说明 | 包括所有功能 | 只保留核心功能 | 适配 Spring Boot 3.X |
|
||||
| 系统功能 | √ | √ | √ |
|
||||
| 基础设施 | √ | √ | √ |
|
||||
| 会员中心 | √ | √ | √ |
|
||||
| 工作流程 | √ | x | 适配中 |
|
||||
| 工作流程 | √ | x | √ |
|
||||
| 数据报表 | √ | x | 适配中 |
|
||||
| 商城系统 | √ | x | √ |
|
||||
| 微信公众号 | √ | x | √ |
|
||||
|
@ -218,6 +220,10 @@
|
|||
|
||||
![功能图](/.image/common/mall-preview.png)
|
||||
|
||||
_前端基于 crmeb uniapp 经过授权重构,优化代码实现,接入芋道快速开发平台_
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/mall-preview/>
|
||||
|
||||
### 会员中心
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|
@ -248,29 +254,29 @@
|
|||
|
||||
### 框架
|
||||
|
||||
| 框架 | 说明 | 版本 | 学习指南 |
|
||||
|---------------------------------------------------------------------------------------------|------------------|-------------|----------------------------------------------------------------|
|
||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.14 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
|
||||
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
|
||||
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.18 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
|
||||
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 | |
|
||||
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.18.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
|
||||
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.24 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
|
||||
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.7.6 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
|
||||
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.5 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
|
||||
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.8.0 | [文档](https://doc.iocoder.cn/bpm/) |
|
||||
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
|
||||
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 1.6.15 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
|
||||
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
|
||||
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.12.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
|
||||
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.7.10 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
|
||||
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
|
||||
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.5.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
|
||||
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.28 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
|
||||
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
|
||||
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
|
||||
| 框架 | 说明 | 版本 | 学习指南 |
|
||||
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
|
||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.16 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
|
||||
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
|
||||
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.19 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
|
||||
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
|
||||
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.18.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
|
||||
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.24 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
|
||||
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.7.11 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
|
||||
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.5 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
|
||||
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.8.0 | [文档](https://doc.iocoder.cn/bpm/) |
|
||||
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
|
||||
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 1.6.15 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
|
||||
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
|
||||
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.12.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
|
||||
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.7.10 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
|
||||
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
|
||||
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.5.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
|
||||
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.30 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
|
||||
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
|
||||
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
|
||||
|
||||
## 🐷 演示图
|
||||
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -30,7 +30,7 @@
|
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>1.8.1-snapshot</revision>
|
||||
<revision>1.8.2-snapshot</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
|
@ -39,8 +39,8 @@
|
|||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
||||
<!-- 看看咋放到 bom 里 -->
|
||||
<lombok.version>1.18.28</lombok.version>
|
||||
<spring.boot.version>2.7.14</spring.boot.version>
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<spring.boot.version>2.7.16</spring.boot.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
-- 增加配置表
|
||||
CREATE TABLE trade_config
|
||||
(
|
||||
id BIGINT AUTO_INCREMENT COMMENT '自增主键' PRIMARY KEY,
|
||||
brokerage_enabled BIT DEFAULT 1 NOT NULL COMMENT '是否启用分佣',
|
||||
brokerage_enabled_condition TINYINT DEFAULT 1 NOT NULL COMMENT '分佣模式:1-人人分销 2-指定分销',
|
||||
brokerage_bind_mode TINYINT DEFAULT 1 NOT NULL COMMENT '分销关系绑定模式: 1-没有推广人,2-新用户, 3-扫码覆盖',
|
||||
brokerage_poster_urls VARCHAR(2000) DEFAULT '' NULL COMMENT '分销海报图地址数组',
|
||||
brokerage_first_percent INT DEFAULT 0 NOT NULL COMMENT '一级返佣比例',
|
||||
brokerage_second_percent INT DEFAULT 0 NOT NULL COMMENT '二级返佣比例',
|
||||
brokerage_withdraw_min_price INT DEFAULT 0 NOT NULL COMMENT '用户提现最低金额',
|
||||
brokerage_withdraw_fee_percent INT DEFAULT 0 NOT NULL COMMENT '提现手续费百分比',
|
||||
brokerage_bank_names VARCHAR(200) DEFAULT '' NOT NULL COMMENT '提现银行(字典类型=brokerage_bank_name)',
|
||||
brokerage_frozen_days INT DEFAULT 7 NOT NULL COMMENT '佣金冻结时间(天)',
|
||||
brokerage_withdraw_types VARCHAR(32) DEFAULT '1,2,3,4' NOT NULL COMMENT '提现方式:1-钱包;2-银行卡;3-微信;4-支付宝',
|
||||
creator VARCHAR(64) DEFAULT '' NULL COMMENT '创建者',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
|
||||
updater VARCHAR(64) DEFAULT '' NULL COMMENT '更新者',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
deleted BIT DEFAULT b'0' NOT NULL COMMENT '是否删除',
|
||||
tenant_id BIGINT DEFAULT 0 NOT NULL COMMENT '租户编号'
|
||||
) COMMENT '交易中心配置';
|
||||
|
||||
# alter table trade_config
|
||||
# add brokerage_withdraw_fee_percent int default 0 not null comment '提现手续费百分比' after brokerage_withdraw_min_price;
|
||||
|
||||
# alter table trade_brokerage_user
|
||||
# add level int not null default 1 comment '等级' after frozen_price;
|
||||
# alter table trade_brokerage_user
|
||||
# add path varchar(2000) null comment '路径' after level;
|
||||
|
||||
|
||||
-- 增加分销用户扩展表
|
||||
create table trade_brokerage_user
|
||||
(
|
||||
id bigint auto_increment comment '用户编号' primary key,
|
||||
bind_user_id bigint null comment '推广员编号',
|
||||
bind_user_time datetime null comment '推广员绑定时间',
|
||||
brokerage_enabled bit default 1 not null comment '是否成为推广员',
|
||||
brokerage_time datetime null comment '成为分销员时间',
|
||||
price int default 0 not null comment '可用佣金',
|
||||
frozen_price int default 0 not null comment '冻结佣金',
|
||||
creator varchar(64) default '' null comment '创建者',
|
||||
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||
updater varchar(64) default '' null comment '更新者',
|
||||
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||
deleted bit default b'0' not null comment '是否删除',
|
||||
tenant_id bigint default 0 not null comment '租户编号'
|
||||
)
|
||||
comment '分销用户';
|
||||
|
||||
create index idx_invite_user_id on trade_brokerage_user (bind_user_id) comment '推广员编号';
|
||||
create index idx_agent on trade_brokerage_user (brokerage_enabled) comment '是否成为推广员';
|
||||
|
||||
|
||||
create table trade_brokerage_record
|
||||
(
|
||||
id int auto_increment comment '编号'
|
||||
primary key,
|
||||
user_id bigint not null comment '用户编号',
|
||||
biz_id varchar(64) default '' not null comment '业务编号',
|
||||
biz_type tinyint default 0 not null comment '业务类型:1-订单,2-提现',
|
||||
title varchar(64) default '' not null comment '标题',
|
||||
price int default 0 not null comment '金额',
|
||||
total_price int default 0 not null comment '当前总佣金',
|
||||
description varchar(500) default '' not null comment '说明',
|
||||
status tinyint default 0 not null comment '状态:0-待结算,1-已结算,2-已取消',
|
||||
frozen_days int default 0 not null comment '冻结时间(天)',
|
||||
unfreeze_time datetime null comment '解冻时间',
|
||||
source_user_level int not null comment '来源用户等级',
|
||||
source_user_id bigint not null comment '来源用户编号',
|
||||
creator varchar(64) default '' null comment '创建者',
|
||||
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||
updater varchar(64) default '' null comment '更新者',
|
||||
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||
deleted bit default b'0' not null comment '是否删除',
|
||||
tenant_id bigint default 0 not null comment '租户编号'
|
||||
)
|
||||
comment '佣金记录';
|
||||
|
||||
create index idx_user_id on trade_brokerage_record (user_id) comment '用户编号';
|
||||
create index idx_biz on trade_brokerage_record (biz_type, biz_id) comment '业务';
|
||||
create index idx_status on trade_brokerage_record (status) comment '状态';
|
||||
|
||||
|
||||
create table trade_brokerage_withdraw
|
||||
(
|
||||
id bigint auto_increment comment '编号'
|
||||
primary key,
|
||||
user_id bigint not null comment '用户编号',
|
||||
price int default 0 not null comment '提现金额',
|
||||
fee_price int default 0 not null comment '提现手续费',
|
||||
total_price int default 0 not null comment '当前总佣金',
|
||||
type tinyint default 0 not null comment '提现类型:1-钱包;2-银行卡;3-微信;4-支付宝',
|
||||
name varchar(64) null comment '真实姓名',
|
||||
account_no varchar(64) null comment '账号',
|
||||
bank_name varchar(100) null comment '银行名称',
|
||||
bank_address varchar(200) null comment '开户地址',
|
||||
account_qr_code_url varchar(512) null comment '收款码',
|
||||
status tinyint(2) default 0 not null comment '状态:0-审核中,10-审核通过 20-审核不通过;预留:11 - 提现成功;21-提现失败',
|
||||
audit_reason varchar(128) null comment '审核驳回原因',
|
||||
audit_time datetime null comment '审核时间',
|
||||
remark varchar(500) null comment '备注',
|
||||
creator varchar(64) default '' null comment '创建者',
|
||||
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
|
||||
updater varchar(64) default '' null comment '更新者',
|
||||
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||
deleted bit default b'0' not null comment '是否删除',
|
||||
tenant_id bigint default 0 not null comment '租户编号'
|
||||
)
|
||||
comment '佣金提现';
|
||||
|
||||
create index idx_user_id on trade_brokerage_withdraw (user_id) comment '用户编号';
|
||||
create index idx_audit_status on trade_brokerage_withdraw (status) comment '状态';
|
||||
|
||||
-- 增加字典
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_enabled_condition', '分佣模式');
|
||||
insert into system_dict_data(dict_type, label, value, sort, remark)
|
||||
values ('brokerage_enabled_condition', '人人分销', 1, 1, '所有用户都可以分销'),
|
||||
('brokerage_enabled_condition', '指定分销', 2, 2, '仅可后台手动设置推广员');
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_bind_mode', '分销关系绑定模式');
|
||||
insert into system_dict_data(dict_type, label, value, sort, remark)
|
||||
values ('brokerage_bind_mode', '没有推广人', 1, 1, '只要用户没有推广人,随时都可以绑定推广关系'),
|
||||
('brokerage_bind_mode', '新用户', 2, 2, '仅新用户注册时才能绑定推广关系'),
|
||||
('brokerage_bind_mode', '扫码覆盖', 3, 3, '如果用户已经有推广人,推广人会被变更');
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_withdraw_type', '佣金提现类型');
|
||||
insert into system_dict_data(dict_type, label, value, sort)
|
||||
values ('brokerage_withdraw_type', '钱包', 1, 1),
|
||||
('brokerage_withdraw_type', '银行卡', 2, 2),
|
||||
('brokerage_withdraw_type', '微信', 3, 3),
|
||||
('brokerage_withdraw_type', '支付宝', 4, 4);
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_record_biz_type', '佣金记录业务类型');
|
||||
insert into system_dict_data(dict_type, label, value, sort)
|
||||
values ('brokerage_record_biz_type', '订单返佣', 1, 1),
|
||||
('brokerage_record_biz_type', '申请提现', 2, 2),
|
||||
('brokerage_record_biz_type', '申请提现驳回', 3, 3);
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_record_status', '佣金记录状态');
|
||||
insert into system_dict_data(dict_type, label, value, sort)
|
||||
values ('brokerage_record_status', '待结算', 0, 0),
|
||||
('brokerage_record_status', '已结算', 1, 1),
|
||||
('brokerage_record_status', '已取消', 2, 2);
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_withdraw_status', '佣金提现状态');
|
||||
insert into system_dict_data(dict_type, label, value, sort, color_type)
|
||||
values ('brokerage_withdraw_status', '审核中', 0, 0, ''),
|
||||
('brokerage_withdraw_status', '审核通过', 10, 10, 'success'),
|
||||
('brokerage_withdraw_status', '提现成功', 11, 11, 'success'),
|
||||
('brokerage_withdraw_status', '审核不通过', 20, 20, 'danger'),
|
||||
('brokerage_withdraw_status', '提现失败', 21, 21, 'danger');
|
||||
|
||||
insert into system_dict_type(type, name)
|
||||
values ('brokerage_bank_name', '佣金提现银行');
|
||||
insert into system_dict_data(dict_type, label, value, sort)
|
||||
values ('brokerage_bank_name', '工商银行', 0, 0),
|
||||
('brokerage_bank_name', '建设银行', 1, 1),
|
||||
('brokerage_bank_name', '农业银行', 2, 2),
|
||||
('brokerage_bank_name', '中国银行', 3, 3),
|
||||
('brokerage_bank_name', '交通银行', 4, 4),
|
||||
('brokerage_bank_name', '招商银行', 5, 5);
|
||||
|
||||
|
||||
-- 交易中心配置:菜单 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
|
||||
VALUES ('交易中心配置', '', 2, 0, 2072, 'config', 'ep:setting', 'trade/config/index', 0, 'TradeConfig');
|
||||
-- 按钮父菜单ID
|
||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('交易中心配置查询', 'trade:config:query', 3, 1, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('交易中心配置保存', 'trade:config:save', 3, 2, @parentId, '', '', '', 0);
|
||||
|
||||
|
||||
-- 增加菜单:分销
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
|
||||
VALUES ('分销', '', 1, 5, 2072, 'brokerage', 'fa-solid:project-diagram', '', 0, '');
|
||||
-- 按钮父菜单ID
|
||||
SELECT @brokerageMenuId := LAST_INSERT_ID();
|
||||
|
||||
-- 增加菜单:分销员
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
|
||||
VALUES ('分销用户', '', 2, 0, @brokerageMenuId, 'brokerage-user', 'fa-solid:user-tie', 'trade/brokerage/user/index', 0,
|
||||
'TradeBrokerageUser');
|
||||
-- 按钮父菜单ID
|
||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户查询', 'trade:brokerage-user:query', 3, 1, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户推广人查询', 'trade:brokerage-user:user-query', 3, 2, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户推广订单查询', 'trade:brokerage-user:order-query', 3, 3, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户修改推广资格', 'trade:brokerage-user:update-brokerage-enable', 3, 4, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户修改推广员', 'trade:brokerage-user:update-bind-user', 3, 5, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('分销用户清除推广员', 'trade:brokerage-user:clear-bind-user', 3, 6, @parentId, '', '', '', 0);
|
||||
|
||||
-- 增加菜单:佣金记录
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
|
||||
VALUES ('佣金记录', '', 2, 1, @brokerageMenuId, 'brokerage-record', 'fa:money', 'trade/brokerage/record/index', 0,
|
||||
'TradeBrokerageRecord');
|
||||
-- 按钮父菜单ID
|
||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('佣金记录查询', 'trade:brokerage-record:query', 3, 1, @parentId, '', '', '', 0);
|
||||
|
||||
-- 增加菜单:佣金提现
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status, component_name)
|
||||
VALUES ('佣金提现', '', 2, 2, @brokerageMenuId, 'brokerage-withdraw', 'fa:credit-card',
|
||||
'trade/brokerage/withdraw/index', 0, 'TradeBrokerageWithdraw');
|
||||
-- 按钮父菜单ID
|
||||
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('佣金提现查询', 'trade:brokerage-withdraw:query', 3, 1, @parentId, '', '', '', 0);
|
||||
INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('佣金提现审核', 'trade:brokerage-withdraw:audit', 3, 2, @parentId, '', '', '', 0);
|
||||
|
||||
-- 站内信模板
|
||||
INSERT INTO `ruoyi-vue-pro`.system_notify_template (name, code, nickname, content, type, params, status)
|
||||
VALUES
|
||||
('佣金提现(审核通过)', 'brokerage_withdraw_audit_approve', 'system', '您在{createTime}提现¥{price}元的申请已通过审核', 2, '["createTime","price"]', 0),
|
||||
('佣金提现(审核不通过)', 'brokerage_withdraw_audit_reject', 'system', '您在{createTime}提现¥{price}元的申请未通过审核,原因:{reason}', 2, '["createTime","price","reason"]', 0);
|
|
@ -1,3 +0,0 @@
|
|||
ALTER TABLE `ruoyi-vue-pro`.`trade_after_sale_log`
|
||||
ADD COLUMN `before_status` int NOT NULL COMMENT '售前状态' AFTER `id`,
|
||||
ADD COLUMN `after_status` int NOT NULL COMMENT '售后状态' AFTER `before_status`;
|
|
@ -1,6 +0,0 @@
|
|||
ALTER TABLE trade_order ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price;
|
||||
ALTER TABLE trade_order ADD COLUMN refund_point int NOT NULL DEFAULT 0 COMMENT '退还的使用积分' AFTER use_point;
|
||||
ALTER TABLE trade_order ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER refund_point;
|
||||
|
||||
ALTER TABLE trade_order_item ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price;
|
||||
ALTER TABLE trade_order_item ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER use_point;
|
|
@ -11,7 +11,7 @@
|
|||
Target Server Version : 80034
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 03/09/2023 19:13:55
|
||||
Date: 24/09/2023 23:23:06
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
@ -72,6 +72,7 @@ CREATE TABLE `QRTZ_CRON_TRIGGERS` (
|
|||
-- Records of QRTZ_CRON_TRIGGERS
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
|
||||
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
|
||||
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
|
||||
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
|
||||
|
@ -135,6 +136,7 @@ CREATE TABLE `QRTZ_JOB_DETAILS` (
|
|||
-- Records of QRTZ_JOB_DETAILS
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000147400104A4F425F48414E444C45525F4E414D4574000764656D6F4A6F627800);
|
||||
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
|
||||
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800);
|
||||
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800);
|
||||
|
@ -191,7 +193,7 @@ CREATE TABLE `QRTZ_SCHEDULER_STATE` (
|
|||
-- Records of QRTZ_SCHEDULER_STATE
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai1690117495401', 1690119854263, 15000);
|
||||
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai.local1694844151505', 1694844218609, 15000);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -285,6 +287,7 @@ CREATE TABLE `QRTZ_TRIGGERS` (
|
|||
-- Records of QRTZ_TRIGGERS
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', 'demoJob', 'DEFAULT', NULL, 1694880000000, -1, 5, 'WAITING', 'CRON', 1694844083000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000A74000F4A4F425F52455452595F434F554E547371007E000A000000017800);
|
||||
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
|
||||
|
@ -360,7 +363,7 @@ CREATE TABLE `infra_api_error_log` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1497 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1544 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_api_error_log
|
||||
|
@ -398,7 +401,7 @@ CREATE TABLE `infra_codegen_column` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1756 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1755 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_codegen_column
|
||||
|
@ -514,7 +517,7 @@ CREATE TABLE `infra_file` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1054 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_file
|
||||
|
@ -545,12 +548,7 @@ CREATE TABLE `infra_file_config` (
|
|||
-- Records of infra_file_config
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库', 1, '我是数据库', b'1', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '本地磁盘', 10, '测试下本地存储', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/file_test\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:57:00', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (11, 'S3 - 七牛云', 20, NULL, b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-03-19 18:00:03', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-10 20:50:41', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-11 20:32:08', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-11 20:32:47', '1', '2023-04-08 09:44:47', b'0');
|
||||
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库', 1, '我是数据库', b'1', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2023-09-15 12:05:12', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -568,7 +566,7 @@ CREATE TABLE `infra_file_content` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 145 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 149 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_file_content
|
||||
|
@ -596,17 +594,17 @@ CREATE TABLE `infra_job` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_job
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2023-07-09 20:51:41', b'0');
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 'Job 示例', 1, 'demoJob', NULL, '* * * L * ?', 1, 1, 0, '1', '2022-09-24 22:31:41', '1', '2022-09-24 22:31:42', b'0');
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', b'0');
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', b'0');
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', b'0');
|
||||
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (20, 'Job 示例', 1, 'demoJob', '', '0 0 0 * * ?', 1, 10, 0, '1', '2023-09-16 14:01:23', '1', '2023-09-16 14:01:23', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -630,7 +628,7 @@ CREATE TABLE `infra_job_log` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 161 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 163 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_job_log
|
||||
|
@ -803,7 +801,7 @@ CREATE TABLE `member_level_record` (
|
|||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员等级记录-用户编号'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_level_record
|
||||
|
@ -828,7 +826,7 @@ CREATE TABLE `member_point_config` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '会员积分配置表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员积分配置表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_point_config
|
||||
|
@ -844,10 +842,10 @@ DROP TABLE IF EXISTS `member_point_record`;
|
|||
CREATE TABLE `member_point_record` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
|
||||
`user_id` bigint NOT NULL COMMENT '用户编号',
|
||||
`biz_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务编码',
|
||||
`biz_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务编码',
|
||||
`biz_type` tinyint NOT NULL COMMENT '业务类型',
|
||||
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '积分标题',
|
||||
`description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '积分描述',
|
||||
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '积分标题',
|
||||
`description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '积分描述',
|
||||
`point` int NOT NULL COMMENT '积分',
|
||||
`total_point` int NOT NULL COMMENT '变动后的积分',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||
|
@ -859,7 +857,7 @@ CREATE TABLE `member_point_record` (
|
|||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `index_userId`(`user_id` ASC) USING BTREE,
|
||||
INDEX `index_title`(`title` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户积分记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_point_record
|
||||
|
@ -872,6 +870,15 @@ INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title
|
|||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 247, 'null', 11, '订单取消', '退单获得 -18 积分', -18, 73, NULL, '2023-08-31 19:56:21', NULL, '2023-08-31 19:56:21', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 247, '80', 10, '订单消费', '下单获得 2099718 积分', 2099718, 2099791, NULL, '2023-08-31 23:43:29', NULL, '2023-08-31 23:43:29', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 247, '81', 10, '订单消费', '下单获得 8398818 积分', 8398818, 10498609, NULL, '2023-08-31 23:46:17', NULL, '2023-08-31 23:46:17', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 247, '85', 12, '订单使用', '下单使用 -30 积分', -30, 10498579, '247', '2023-09-20 17:11:36', '247', '2023-09-20 17:11:36', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 247, '86', 12, '订单使用', '下单使用 -30 积分', -30, 10498549, '247', '2023-09-20 19:32:59', '247', '2023-09-20 19:32:59', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 247, '87', 12, '订单使用', '下单使用 -30 积分', -30, 10498519, '247', '2023-09-20 23:11:21', '247', '2023-09-20 23:11:21', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (11, 247, '88', 12, '订单使用', '下单使用 -30 积分', -30, 10498489, '247', '2023-09-20 23:20:21', '247', '2023-09-20 23:20:21', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (12, 247, '89', 12, '订单使用', '下单使用 -30 积分', -30, 10498459, '247', '2023-09-23 23:51:40', '247', '2023-09-23 23:51:40', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (13, 247, '90', 12, '订单使用', '下单使用 -30 积分', -30, 10498429, '247', '2023-09-23 23:52:34', '247', '2023-09-23 23:52:34', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (14, 247, '91', 12, '订单使用', '下单使用 -30 积分', -30, 10498399, '247', '2023-09-23 23:54:18', '247', '2023-09-23 23:54:18', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 247, '92', 12, '订单使用', '下单使用 -30 积分', -30, 10498369, '247', '2023-09-23 23:55:33', '247', '2023-09-23 23:55:33', b'0', 1);
|
||||
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 247, '93', 12, '订单使用', '下单使用 -30 积分', -30, 10498339, '247', '2023-09-23 23:56:53', '247', '2023-09-23 23:56:53', b'0', 1);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -890,7 +897,7 @@ CREATE TABLE `member_sign_in_config` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '签到规则';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '签到规则';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_sign_in_config
|
||||
|
@ -919,20 +926,23 @@ CREATE TABLE `member_sign_in_record` (
|
|||
`user_id` int NULL DEFAULT NULL COMMENT '签到用户',
|
||||
`day` int NULL DEFAULT NULL COMMENT '第几天签到',
|
||||
`point` int NULL DEFAULT NULL COMMENT '签到的分数',
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '签到时间',
|
||||
`update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
|
||||
`tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '租户id',
|
||||
`deleted` int NULL DEFAULT 0 COMMENT '是否删除',
|
||||
`creator` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人',
|
||||
`updater` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '签到记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '签到记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_sign_in_record
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 247, 12, 1212, '2023-06-10 20:01:27', '2023-08-20 11:29:50', '1', 0, '1', '1');
|
||||
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 247, 1, 123, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
|
||||
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 247, 12, 12, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
|
||||
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 247, 12, 1212, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
|
||||
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 247, 12, 1212, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -972,7 +982,7 @@ CREATE TABLE `member_user` (
|
|||
`login_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '最后登录IP',
|
||||
`login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间',
|
||||
`nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户昵称',
|
||||
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像',
|
||||
`avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像',
|
||||
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '真实名字',
|
||||
`sex` tinyint NULL DEFAULT 0 COMMENT '用户性别',
|
||||
`area_id` bigint NULL DEFAULT NULL COMMENT '所在地',
|
||||
|
@ -989,9 +999,8 @@ CREATE TABLE `member_user` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 248 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '会员用户';
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 249 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '会员用户';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of member_user
|
||||
|
@ -1188,8 +1197,8 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
|
|||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1166, 2, '折扣', '2', 'promotion_discount_type', 0, 'primary', '', '优惠类型 - 折扣', '1', '2022-11-01 12:46:51', '1', '2022-11-01 12:50:08', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1167, 1, '固定日期', '1', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 固定日期', '1', '2022-11-02 00:07:34', '1', '2022-11-04 00:07:49', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1168, 2, '领取之后', '2', 'promotion_coupon_template_validity_type', 0, 'default', '', '优惠劵模板的有限期类型 - 领取之后', '1', '2022-11-02 00:07:54', '1', '2022-11-04 00:07:52', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1169, 1, '通用卷', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-01 23:42:49', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1170, 2, '商品卷', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-01 23:42:54', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1169, 1, '通用券', '1', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 全部商品参与', '1', '2022-11-02 00:28:22', '1', '2023-09-01 23:42:49', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1170, 2, '商品券', '2', 'promotion_product_scope', 0, 'default', '', '营销的商品范围 - 指定商品参与', '1', '2022-11-02 00:28:34', '1', '2023-09-01 23:42:54', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1171, 1, '已领取', '1', 'promotion_coupon_status', 0, 'primary', '', '优惠劵的状态 - 已领取', '1', '2022-11-04 00:15:08', '1', '2022-11-04 19:16:04', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1172, 2, '已使用', '2', 'promotion_coupon_status', 0, 'success', '', '优惠劵的状态 - 已使用', '1', '2022-11-04 00:15:21', '1', '2022-11-04 19:16:08', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1173, 3, '已过期', '3', 'promotion_coupon_status', 0, 'info', '', '优惠劵的状态 - 已过期', '1', '2022-11-04 00:15:43', '1', '2022-11-04 19:16:12', b'0');
|
||||
|
@ -1284,7 +1293,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
|
|||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1355, 5, '抽奖奖励', '5', 'member_experience_biz_type', 0, '', '', NULL, '', '2023-08-22 12:41:01', '', '2023-08-22 12:41:01', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1356, 1, '快递发货', '1', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:04:55', '1', '2023-08-23 00:04:55', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1357, 2, '用户自提', '2', 'trade_delivery_type', 0, '', '', '', '1', '2023-08-23 00:05:05', '1', '2023-08-23 00:05:05', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '品类卷', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-01 23:43:07', b'0');
|
||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '品类券', '3', 'promotion_product_scope', 0, 'default', '', '', '1', '2023-09-01 23:43:07', '1', '2023-09-01 23:43:07', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -1305,7 +1314,7 @@ CREATE TABLE `system_dict_type` (
|
|||
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 176 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 175 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_dict_type
|
||||
|
@ -1389,7 +1398,7 @@ CREATE TABLE `system_error_code` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5933 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 5932 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_error_code
|
||||
|
@ -1418,7 +1427,7 @@ CREATE TABLE `system_login_log` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2375 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2482 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_login_log
|
||||
|
@ -1483,7 +1492,7 @@ CREATE TABLE `system_mail_log` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 354 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 355 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_mail_log
|
||||
|
@ -2116,7 +2125,7 @@ CREATE TABLE `system_oauth2_access_token` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2597 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2721 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_access_token
|
||||
|
@ -2238,7 +2247,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 896 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 991 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_refresh_token
|
||||
|
@ -2278,7 +2287,7 @@ CREATE TABLE `system_operate_log` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 8321 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 8338 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_operate_log
|
||||
|
@ -3286,7 +3295,7 @@ CREATE TABLE `system_sms_code` (
|
|||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 501 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 513 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_sms_code
|
||||
|
@ -3329,7 +3338,7 @@ CREATE TABLE `system_sms_log` (
|
|||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 365 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 381 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_sms_log
|
||||
|
@ -3401,7 +3410,7 @@ CREATE TABLE `system_social_user` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_social_user
|
||||
|
@ -3426,7 +3435,7 @@ CREATE TABLE `system_social_user_bind` (
|
|||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 39 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 76 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_social_user_bind
|
||||
|
@ -3462,8 +3471,8 @@ CREATE TABLE `system_tenant` (
|
|||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'https://www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2022-02-23 12:15:11', b'0');
|
||||
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-07-25 23:05:38', b'0');
|
||||
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-07-25 23:53:16', b'0');
|
||||
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-09-16 16:59:42', b'0');
|
||||
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-09-16 16:59:27', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -3602,10 +3611,10 @@ CREATE TABLE `system_users` (
|
|||
-- Records of system_users
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-09-02 00:03:37', 'admin', '2021-01-05 17:03:47', NULL, '2023-09-02 00:03:37', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-09-24 12:03:56', 'admin', '2021-01-05 17:03:47', NULL, '2023-09-24 12:03:56', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '127.0.0.1', '2022-05-28 15:43:17', '', '2021-01-21 02:13:53', NULL, '2022-07-09 09:00:33', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', b'0', 1);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
|
||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
|
||||
|
|
|
@ -5815,8 +5815,7 @@ GO
|
|||
|
||||
CREATE TABLE [dbo].[system_dict_type]
|
||||
(
|
||||
[
|
||||
id]
|
||||
[id]
|
||||
bigint
|
||||
IDENTITY
|
||||
(
|
||||
|
|
|
@ -14,16 +14,16 @@
|
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>1.8.1-snapshot</revision>
|
||||
<revision>1.8.2-snapshot</revision>
|
||||
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.14</spring.boot.version>
|
||||
<spring.boot.version>2.7.16</spring.boot.version>
|
||||
<!-- Web 相关 -->
|
||||
<springdoc.version>1.6.15</springdoc.version>
|
||||
<knife4j.version>4.1.0</knife4j.version>
|
||||
<springdoc.version>1.7.0</springdoc.version>
|
||||
<knife4j.version>4.3.0</knife4j.version>
|
||||
<servlet.versoin>2.5</servlet.versoin>
|
||||
<!-- DB 相关 -->
|
||||
<druid.version>1.2.18</druid.version>
|
||||
<druid.version>1.2.19</druid.version>
|
||||
<mybatis-plus.version>3.5.3.2</mybatis-plus.version>
|
||||
<mybatis-plus-generator.version>3.5.3.2</mybatis-plus-generator.version>
|
||||
<dynamic-datasource.version>3.6.1</dynamic-datasource.version>
|
||||
|
@ -44,33 +44,32 @@
|
|||
<!-- Bpm 工作流相关 -->
|
||||
<flowable.version>6.8.0</flowable.version>
|
||||
<!-- 工具类相关 -->
|
||||
<captcha-plus.version>1.0.6</captcha-plus.version>
|
||||
<jsoup.version>1.15.4</jsoup.version>
|
||||
<lombok.version>1.18.28</lombok.version>
|
||||
<captcha-plus.version>1.0.8</captcha-plus.version>
|
||||
<jsoup.version>1.16.1</jsoup.version>
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<hutool.version>5.8.20</hutool.version>
|
||||
<hutool.version>5.8.22</hutool.version>
|
||||
<easyexcel.verion>3.3.2</easyexcel.verion>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<screw.version>1.0.5</screw.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<guava.version>32.0.1-jre</guava.version>
|
||||
<guava.version>32.1.2-jre</guava.version>
|
||||
<guice.version>5.1.0</guice.version>
|
||||
<transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
|
||||
<commons-net.version>3.9.0</commons-net.version>
|
||||
<jsch.version>0.1.55</jsch.version>
|
||||
<tika-core.version>2.7.0</tika-core.version>
|
||||
<netty-all.version>4.1.90.Final</netty-all.version>
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<!-- 三方云服务相关 -->
|
||||
<okio.version>3.0.0</okio.version>
|
||||
<okhttp3.version>4.10.0</okhttp3.version>
|
||||
<okio.version>3.5.0</okio.version>
|
||||
<okhttp3.version>4.11.0</okhttp3.version>
|
||||
<commons-io.version>2.11.0</commons-io.version>
|
||||
<minio.version>8.5.4</minio.version>
|
||||
<aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
|
||||
<minio.version>8.5.6</minio.version>
|
||||
<aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>
|
||||
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
|
||||
<tencentcloud-sdk-java.version>3.1.758</tencentcloud-sdk-java.version>
|
||||
<justauth.version>1.0.3</justauth.version>
|
||||
<jimureport.version>1.5.8</jimureport.version>
|
||||
<tencentcloud-sdk-java.version>3.1.853</tencentcloud-sdk-java.version>
|
||||
<justauth.version>1.0.5</justauth.version>
|
||||
<jimureport.version>1.6.1</jimureport.version>
|
||||
<xercesImpl.version>2.12.2</xercesImpl.version>
|
||||
<weixin-java.version>4.5.0</weixin-java.version>
|
||||
</properties>
|
||||
|
@ -233,6 +232,12 @@
|
|||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -337,6 +342,12 @@
|
|||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
|
||||
<version>${spring-boot-admin.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-server-cloud</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
|
@ -519,12 +530,6 @@
|
|||
<version>${jsch.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>${netty-all.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xingyuv</groupId>
|
||||
<artifactId>spring-boot-starter-captcha-plus</artifactId>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.14</spring.boot.version>
|
||||
<spring.boot.version>2.7.16</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.20</version>
|
||||
<version>5.8.22</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.14</spring.boot.version>
|
||||
<spring.boot.version>2.7.16</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.20</version>
|
||||
<version>5.8.22</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -23,6 +23,10 @@ public class LocalDateTimeUtils {
|
|||
return LocalDateTime.now().plus(duration);
|
||||
}
|
||||
|
||||
public static LocalDateTime minusTime(Duration duration) {
|
||||
return LocalDateTime.now().minus(duration);
|
||||
}
|
||||
|
||||
public static boolean beforeNow(LocalDateTime date) {
|
||||
return date.isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -23,7 +22,6 @@ import java.util.List;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@UtilityClass
|
||||
@Slf4j
|
||||
public class JsonUtils {
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
|||
*/
|
||||
public final void init() {
|
||||
doInit();
|
||||
log.info("[init][客户端({}) 初始化完成]", getId());
|
||||
log.debug("[init][客户端({}) 初始化完成]", getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,7 +40,7 @@ public abstract class AbstractSmsClient implements SmsClient {
|
|||
*/
|
||||
public final void init() {
|
||||
doInit();
|
||||
log.info("[init][配置({}) 初始化完成]", properties);
|
||||
log.debug("[init][配置({}) 初始化完成]", properties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package cn.iocoder.yudao.framework.tenant.config;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
|
||||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager;
|
||||
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
|
||||
|
@ -20,8 +17,6 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
|||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration {
|
|||
// ========== Job ==========
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (!(bean instanceof JobHandler)) {
|
||||
return bean;
|
||||
}
|
||||
// 有 TenantJob 注解的情况下,才会进行处理
|
||||
if (!AnnotationUtil.hasAnnotation(bean.getClass(), TenantJob.class)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
// 使用 TenantJobHandlerDecorator 装饰
|
||||
return new TenantJobHandlerDecorator(tenantFrameworkService, (JobHandler) bean);
|
||||
}
|
||||
|
||||
};
|
||||
public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
|
||||
return new TenantJobAspect(tenantFrameworkService);
|
||||
}
|
||||
|
||||
// ========== Redis ==========
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.lang.annotation.Target;
|
|||
/**
|
||||
* 多租户 Job 注解
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TenantJob {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.job;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 多租户 JobHandler AOP
|
||||
* 任务执行时,会按照租户逐个执行 Job 的逻辑
|
||||
*
|
||||
* 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Aspect
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TenantJobAspect {
|
||||
|
||||
private final TenantFrameworkService tenantFrameworkService;
|
||||
|
||||
@Around("@annotation(tenantJob)")
|
||||
public String around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) {
|
||||
// 获得租户列表
|
||||
List<Long> tenantIds = tenantFrameworkService.getTenantIds();
|
||||
if (CollUtil.isEmpty(tenantIds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 逐个租户,执行 Job
|
||||
Map<Long, String> results = new ConcurrentHashMap<>();
|
||||
tenantIds.parallelStream().forEach(tenantId -> {
|
||||
// TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况
|
||||
TenantUtils.execute(tenantId, () -> {
|
||||
try {
|
||||
joinPoint.proceed();
|
||||
} catch (Throwable e) {
|
||||
results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
return JsonUtils.toJsonString(results);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.job;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 多租户 JobHandler 装饰器
|
||||
* 任务执行时,会按照租户逐个执行 Job 的逻辑
|
||||
*
|
||||
* 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class TenantJobHandlerDecorator implements JobHandler {
|
||||
|
||||
private final TenantFrameworkService tenantFrameworkService;
|
||||
/**
|
||||
* 被装饰的 Job
|
||||
*/
|
||||
private final JobHandler jobHandler;
|
||||
|
||||
@Override
|
||||
public final String execute(String param) throws Exception {
|
||||
// 获得租户列表
|
||||
List<Long> tenantIds = tenantFrameworkService.getTenantIds();
|
||||
if (CollUtil.isEmpty(tenantIds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 逐个租户,执行 Job
|
||||
Map<Long, String> results = new ConcurrentHashMap<>();
|
||||
tenantIds.parallelStream().forEach(tenantId -> { // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况
|
||||
try {
|
||||
// 设置租户
|
||||
TenantContextHolder.setTenantId(tenantId);
|
||||
// 执行 Job
|
||||
String result = jobHandler.execute(param);
|
||||
// 添加结果
|
||||
results.put(tenantId, result);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
TenantContextHolder.clear();
|
||||
}
|
||||
});
|
||||
return JsonUtils.toJsonString(results);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.redis;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
|
@ -15,7 +16,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
|
|||
* @author airhead
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantRedisCacheManager extends RedisCacheManager {
|
||||
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
|
||||
|
||||
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
|
||||
RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.job;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* 验证 job 租户逻辑
|
||||
* {@link TenantJobHandlerDecorator}
|
||||
*
|
||||
* @author gaibu
|
||||
*/
|
||||
public class TenantJobTest extends BaseMockitoUnitTest {
|
||||
|
||||
@Mock
|
||||
TenantFrameworkService tenantFrameworkService;
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
// 准备测试租户 id
|
||||
List<Long> tenantIds = Lists.newArrayList(1L, 2L, 3L);
|
||||
// mock 数据
|
||||
Mockito.doReturn(tenantIds).when(tenantFrameworkService).getTenantIds();
|
||||
// 准备测试任务
|
||||
TestJob testJob = new TestJob();
|
||||
// 创建任务装饰器
|
||||
TenantJobHandlerDecorator tenantJobHandlerDecorator = new TenantJobHandlerDecorator(tenantFrameworkService, testJob);
|
||||
|
||||
// 执行任务
|
||||
tenantJobHandlerDecorator.execute(null);
|
||||
|
||||
// 断言返回值
|
||||
assertEquals(testJob.getTenantIds(), tenantIds);
|
||||
}
|
||||
}
|
|
@ -9,12 +9,12 @@ import java.util.List;
|
|||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@Component
|
||||
@TenantJob // 标记多租户
|
||||
public class TestJob implements JobHandler {
|
||||
|
||||
private final List<Long> tenantIds = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Override
|
||||
@TenantJob // 标记多租户
|
||||
public String execute(String param) throws Exception {
|
||||
tenantIds.add(TenantContextHolder.getTenantId());
|
||||
return "success";
|
||||
|
@ -24,4 +24,5 @@ public class TestJob implements JobHandler {
|
|||
CollUtil.sort(tenantIds, Long::compareTo);
|
||||
return tenantIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public abstract class AbstractFileClient<Config extends FileClientConfig> implem
|
|||
*/
|
||||
public final void init() {
|
||||
doInit();
|
||||
log.info("[init][配置({}) 初始化完成]", config);
|
||||
log.debug("[init][配置({}) 初始化完成]", config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
package cn.iocoder.yudao.framework.flowable.core.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 流程模型转操作工具类
|
||||
*/
|
||||
public class BpmnModelUtils {
|
||||
|
||||
/**
|
||||
* 根据节点,获取入口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 入口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getIncomingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取出口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 出口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getOutgoingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程元素信息
|
||||
*
|
||||
* @param model bpmnModel 对象
|
||||
* @param flowElementId 元素 ID
|
||||
* @return 元素信息
|
||||
*/
|
||||
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
|
||||
Process process = model.getMainProcess();
|
||||
return process.getFlowElement(flowElementId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 BPMN 流程中,指定的元素们
|
||||
*
|
||||
* @param model
|
||||
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较 两个bpmnModel 是否相同
|
||||
* @param oldModel 老的bpmn model
|
||||
* @param newModel 新的bpmn model
|
||||
*/
|
||||
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
|
||||
// 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
|
||||
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 bpmnModel 转换成 byte[]
|
||||
* @param model bpmnModel
|
||||
*/
|
||||
public static byte[] getBpmnBytes(BpmnModel model) {
|
||||
if (model == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
return converter.convertToXML(model);
|
||||
}
|
||||
|
||||
// ========== 遍历相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 找到 source 节点之前的所有用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 已找到的用户任务节点
|
||||
* @return 用户任务节点 数组
|
||||
*/
|
||||
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 类型为用户节点,则新增父级节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
}
|
||||
// 类型为子流程,则添加子流程开始节点出口处相连的节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
// 获取子流程用户任务节点
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 用户任务节点
|
||||
*/
|
||||
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param target 目标节点
|
||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||
// 不能是开始事件和子流程
|
||||
if (source instanceof StartEvent && isInEventSubprocess(source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (CollUtil.isEmpty(sequenceFlows)) {
|
||||
return true;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (visitedElements.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
visitedElements.add(sequenceFlow.getId());
|
||||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
||||
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
|
||||
if (target.getId().equals(sourceFlowElement.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 如果目标节点为并行网关,则不继续
|
||||
if (sourceFlowElement instanceof ParallelGateway) {
|
||||
return false;
|
||||
}
|
||||
// 否则就继续迭代
|
||||
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前节点是否属于不同的子流程
|
||||
*
|
||||
* @param flowElement 被判断的节点
|
||||
* @return true 表示属于子流程
|
||||
*/
|
||||
private static boolean isInEventSubprocess(FlowElement flowElement) {
|
||||
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
|
||||
while (flowElementsContainer != null) {
|
||||
if (flowElementsContainer instanceof EventSubProcess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flowElementsContainer instanceof FlowElement) {
|
||||
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
|
||||
} else {
|
||||
flowElementsContainer = null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 子级任务节点列表
|
||||
*/
|
||||
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
|
||||
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.flowable.core.util;
|
||||
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
*
|
||||
|
@ -26,49 +19,6 @@ public class FlowableUtils {
|
|||
Authentication.setAuthenticatedUserId(null);
|
||||
}
|
||||
|
||||
// ========== BPMN 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 获得 BPMN 流程中,指定的元素们
|
||||
*
|
||||
* @param model
|
||||
* @param clazz 指定元素。例如说,{@link org.flowable.bpmn.model.UserTask}、{@link org.flowable.bpmn.model.Gateway} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较 两个bpmnModel 是否相同
|
||||
* @param oldModel 老的bpmn model
|
||||
* @param newModel 新的bpmn model
|
||||
*/
|
||||
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
|
||||
// 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
|
||||
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 bpmnModel 转换成 byte[]
|
||||
* @param model bpmnModel
|
||||
*/
|
||||
public static byte[] getBpmnBytes(BpmnModel model) {
|
||||
if (model == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
return converter.convertToXML(model);
|
||||
}
|
||||
|
||||
// ========== Execution 相关的工具方法 ==========
|
||||
|
||||
public static String formatCollectionVariable(String activityId) {
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
package cn.iocoder.yudao.framework.quartz.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.Scheduler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 定时任务 Configuration
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableScheduling // 开启 Spring 自带的定时任务
|
||||
@Slf4j
|
||||
public class YudaoQuartzAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SchedulerManager schedulerManager(Scheduler scheduler) {
|
||||
return new SchedulerManager(scheduler);
|
||||
public SchedulerManager schedulerManager(Optional<Scheduler> scheduler) {
|
||||
if (!scheduler.isPresent()) {
|
||||
log.info("[定时任务 - 已禁用][参考 https://doc.iocoder.cn/job/ 开启]");
|
||||
return new SchedulerManager(null);
|
||||
}
|
||||
return new SchedulerManager(scheduler.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.quartz.core.enums.JobDataKeyEnum;
|
|||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker;
|
||||
import org.quartz.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
|
||||
/**
|
||||
* {@link org.quartz.Scheduler} 的管理器,负责创建任务
|
||||
*
|
||||
|
@ -37,6 +40,7 @@ public class SchedulerManager {
|
|||
public void addJob(Long jobId, String jobHandlerName, String jobHandlerParam, String cronExpression,
|
||||
Integer retryCount, Integer retryInterval)
|
||||
throws SchedulerException {
|
||||
validateScheduler();
|
||||
// 创建 JobDetail 对象
|
||||
JobDetail jobDetail = JobBuilder.newJob(JobHandlerInvoker.class)
|
||||
.usingJobData(JobDataKeyEnum.JOB_ID.name(), jobId)
|
||||
|
@ -61,6 +65,7 @@ public class SchedulerManager {
|
|||
public void updateJob(String jobHandlerName, String jobHandlerParam, String cronExpression,
|
||||
Integer retryCount, Integer retryInterval)
|
||||
throws SchedulerException {
|
||||
validateScheduler();
|
||||
// 创建新 Trigger 对象
|
||||
Trigger newTrigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
|
||||
// 修改调度
|
||||
|
@ -74,6 +79,7 @@ public class SchedulerManager {
|
|||
* @throws SchedulerException 删除异常
|
||||
*/
|
||||
public void deleteJob(String jobHandlerName) throws SchedulerException {
|
||||
validateScheduler();
|
||||
scheduler.deleteJob(new JobKey(jobHandlerName));
|
||||
}
|
||||
|
||||
|
@ -84,6 +90,7 @@ public class SchedulerManager {
|
|||
* @throws SchedulerException 暂停异常
|
||||
*/
|
||||
public void pauseJob(String jobHandlerName) throws SchedulerException {
|
||||
validateScheduler();
|
||||
scheduler.pauseJob(new JobKey(jobHandlerName));
|
||||
}
|
||||
|
||||
|
@ -94,6 +101,7 @@ public class SchedulerManager {
|
|||
* @throws SchedulerException 启动异常
|
||||
*/
|
||||
public void resumeJob(String jobHandlerName) throws SchedulerException {
|
||||
validateScheduler();
|
||||
scheduler.resumeJob(new JobKey(jobHandlerName));
|
||||
scheduler.resumeTrigger(new TriggerKey(jobHandlerName));
|
||||
}
|
||||
|
@ -108,11 +116,12 @@ public class SchedulerManager {
|
|||
*/
|
||||
public void triggerJob(Long jobId, String jobHandlerName, String jobHandlerParam)
|
||||
throws SchedulerException {
|
||||
validateScheduler();
|
||||
// 触发任务
|
||||
JobDataMap data = new JobDataMap(); // 无需重试,所以不设置 retryCount 和 retryInterval
|
||||
data.put(JobDataKeyEnum.JOB_ID.name(), jobId);
|
||||
data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName);
|
||||
data.put(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam);
|
||||
// 触发任务
|
||||
scheduler.triggerJob(new JobKey(jobHandlerName), data);
|
||||
}
|
||||
|
||||
|
@ -127,4 +136,11 @@ public class SchedulerManager {
|
|||
.build();
|
||||
}
|
||||
|
||||
private void validateScheduler() {
|
||||
if (scheduler == null) {
|
||||
throw exception0(NOT_IMPLEMENTED.getCode(),
|
||||
"[定时任务 - 已禁用][参考 https://doc.iocoder.cn/job/ 开启]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
|||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-spring-boot2</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@ package cn.iocoder.yudao.framework.lock4j.config;
|
|||
import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;
|
||||
import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration(before = LockAutoConfiguration.class)
|
||||
@ConditionalOnClass(name = "com.baomidou.lock.annotation.Lock4j")
|
||||
public class YudaoLock4jConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
<artifactId>spring-boot-starter-cache</artifactId> <!-- 实现对 Caches 的自动化配置 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
|
|
|
@ -45,7 +45,7 @@ public class TimeoutRedisCacheManager extends RedisCacheManager {
|
|||
Duration duration = parseDuration(names[1]);
|
||||
cacheConfig = cacheConfig.entryTtl(duration);
|
||||
}
|
||||
return super.createRedisCache(names[0], cacheConfig);
|
||||
return super.createRedisCache(name, cacheConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,13 +55,6 @@
|
|||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务保障相关 -->
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-ratelimiter</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
|
||||
</dependency>
|
||||
|
||||
<!-- xss -->
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.framework.jackson.config;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeDeserializer;
|
||||
import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeSerializer;
|
||||
|
@ -11,51 +12,41 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
|||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
@AutoConfiguration
|
||||
@Slf4j
|
||||
public class YudaoJacksonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor objectMapperBeanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||
public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) {
|
||||
// 1.1 创建 SimpleModule 对象
|
||||
SimpleModule simpleModule = new SimpleModule();
|
||||
simpleModule
|
||||
// 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
|
||||
.addSerializer(Long.class, NumberSerializer.INSTANCE)
|
||||
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
|
||||
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
|
||||
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
|
||||
// 新增 LocalDateTime 序列化、反序列化规则
|
||||
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
||||
// 1.2 注册到 objectMapper
|
||||
objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule));
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (!(bean instanceof ObjectMapper)) {
|
||||
return bean;
|
||||
}
|
||||
ObjectMapper objectMapper = (ObjectMapper) bean;
|
||||
SimpleModule simpleModule = new SimpleModule();
|
||||
/*
|
||||
* 1. 新增Long类型序列化规则,数值超过2^53-1,在JS会出现精度丢失问题,因此Long自动序列化为字符串类型
|
||||
* 2. 新增LocalDateTime序列化、反序列化规则
|
||||
*/
|
||||
simpleModule
|
||||
.addSerializer(Long.class, NumberSerializer.INSTANCE)
|
||||
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
|
||||
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
|
||||
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
|
||||
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
||||
|
||||
objectMapper.registerModules(simpleModule);
|
||||
|
||||
JsonUtils.init(objectMapper);
|
||||
log.info("初始化 jackson 自动配置");
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
// 2. 设置 objectMapper 到 JsonUtils {
|
||||
JsonUtils.init(CollUtil.getFirst(objectMappers));
|
||||
log.info("[init][初始化 JsonUtils 成功]");
|
||||
return new JsonUtils();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
@ -33,6 +32,7 @@ import javax.validation.ConstraintViolationException;
|
|||
import javax.validation.ValidationException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
|
||||
|
||||
|
@ -83,9 +83,6 @@ public class GlobalExceptionHandler {
|
|||
if (ex instanceof HttpRequestMethodNotSupportedException) {
|
||||
return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);
|
||||
}
|
||||
if (ex instanceof RequestNotPermitted) {
|
||||
return requestNotPermittedExceptionHandler(request, (RequestNotPermitted) ex);
|
||||
}
|
||||
if (ex instanceof ServiceException) {
|
||||
return serviceExceptionHandler((ServiceException) ex);
|
||||
}
|
||||
|
@ -186,8 +183,7 @@ public class GlobalExceptionHandler {
|
|||
/**
|
||||
* 处理 Resilience4j 限流抛出的异常
|
||||
*/
|
||||
@ExceptionHandler(value = RequestNotPermitted.class)
|
||||
public CommonResult<?> requestNotPermittedExceptionHandler(HttpServletRequest req, RequestNotPermitted ex) {
|
||||
public CommonResult<?> requestNotPermittedExceptionHandler(HttpServletRequest req, Throwable ex) {
|
||||
log.warn("[requestNotPermittedExceptionHandler][url({}) 访问过于频繁]", req.getRequestURL(), ex);
|
||||
return CommonResult.error(TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
@ -226,7 +222,12 @@ public class GlobalExceptionHandler {
|
|||
return tableNotExistsResult;
|
||||
}
|
||||
|
||||
// 情况二:处理异常
|
||||
// 情况二:部分特殊的库的处理
|
||||
if (Objects.equals("io.github.resilience4j.ratelimiter.RequestNotPermitted", ex.getClass().getName())) {
|
||||
return requestNotPermittedExceptionHandler(req, ex);
|
||||
}
|
||||
|
||||
// 情况三:处理异常
|
||||
log.error("[defaultExceptionHandler]", ex);
|
||||
// 插入异常日志
|
||||
this.createExceptionLog(req, ex);
|
||||
|
|
|
@ -21,6 +21,7 @@ import static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.cr
|
|||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(XssProperties.class)
|
||||
@ConditionalOnProperty(prefix = "yudao.xss", name = "enable", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用
|
||||
public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
|
||||
cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
|
||||
com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration
|
||||
cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
|
||||
cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
|
||||
cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration
|
||||
|
|
|
@ -4,61 +4,66 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
|||
|
||||
/**
|
||||
* Bpm 错误码枚举类
|
||||
*
|
||||
* <p>
|
||||
* bpm 系统,使用 1-009-000-000 段
|
||||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 通用流程处理 模块 1009000000 ==========
|
||||
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1009000002, "获取高亮流程图异常");
|
||||
// ========== 通用流程处理 模块 1-009-000-000 ==========
|
||||
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1_009_000_002, "获取高亮流程图异常");
|
||||
|
||||
// ========== OA 流程模块 1009001000 ==========
|
||||
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1009001001, "请假申请不存在");
|
||||
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1009001002, "项目经理岗位未设置");
|
||||
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1009001009, "部门的项目经理不存在");
|
||||
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1009001004, "部门经理岗位未设置");
|
||||
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1009001005, "部门的部门经理不存在");
|
||||
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1009001006, "HR岗位未设置");
|
||||
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1009001007, "请假天数必须>=1");
|
||||
// ========== OA 流程模块 1-009-001-000 ==========
|
||||
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在");
|
||||
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_002, "项目经理岗位未设置");
|
||||
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_009, "部门的项目经理不存在");
|
||||
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_004, "部门经理岗位未设置");
|
||||
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在");
|
||||
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置");
|
||||
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1");
|
||||
|
||||
// ========== 流程模型 1009002000 ==========
|
||||
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
|
||||
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在");
|
||||
ErrorCode MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1009002003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1009002004, "部署流程失败," +
|
||||
// ========== 流程模型 1-009-002-000 ==========
|
||||
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程");
|
||||
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在");
|
||||
ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
|
||||
"原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1009003005, "流程定义部署失败,原因:信息未发生变化");
|
||||
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化");
|
||||
|
||||
// ========== 流程定义 1009003000 ==========
|
||||
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
|
||||
ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
|
||||
ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在");
|
||||
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态");
|
||||
ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在");
|
||||
// ========== 流程定义 1-009-003-000 ==========
|
||||
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
|
||||
ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
|
||||
ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
|
||||
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态");
|
||||
ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在");
|
||||
|
||||
// ========== 流程实例 1009004000 ==========
|
||||
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
|
||||
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中");
|
||||
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的");
|
||||
// ========== 流程实例 1-009-004-000 ==========
|
||||
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在");
|
||||
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
|
||||
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
|
||||
|
||||
// ========== 流程任务 1009005000 ==========
|
||||
ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009005000, "审批任务失败,原因:该任务不处于未审批");
|
||||
ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1009005001, "审批任务失败,原因:该任务的审批人不是你");
|
||||
// ========== 流程任务 1-009-005-000 ==========
|
||||
ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
|
||||
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
|
||||
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
|
||||
ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在");
|
||||
ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转");
|
||||
ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人");
|
||||
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
|
||||
|
||||
// ========== 流程任务分配规则 1009006000 ==========
|
||||
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");
|
||||
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在");
|
||||
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改");
|
||||
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!");
|
||||
ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在");
|
||||
// ========== 流程任务分配规则 1-009-006-000 ==========
|
||||
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则");
|
||||
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在");
|
||||
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1_009_006_002, "只有流程模型的任务分配规则,才允许被修改");
|
||||
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
|
||||
ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1_009_006_004, "操作失败,原因:任务分配脚本({}) 不存在");
|
||||
|
||||
// ========== 动态表单模块 1009010000 ==========
|
||||
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");
|
||||
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009010001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
|
||||
// ========== 动态表单模块 1-009-010-000 ==========
|
||||
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
|
||||
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
|
||||
|
||||
// ========== 用户组模块 1009011000 ==========
|
||||
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1009011000, "用户组不存在");
|
||||
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1009011001, "名字为【{}】的用户组已被禁用");
|
||||
// ========== 用户组模块 1-009-011-000 ==========
|
||||
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户组不存在");
|
||||
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户组已被禁用");
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ public enum BpmProcessInstanceResultEnum {
|
|||
|
||||
// ========== 流程任务独有的状态 ==========
|
||||
|
||||
BACK(5, "退回/驳回");
|
||||
BACK(5, "驳回"), // 退回
|
||||
DELEGATE(6, "委派");
|
||||
|
||||
/**
|
||||
* 结果
|
||||
|
|
|
@ -75,4 +75,29 @@ public class BpmTaskController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get-return-list")
|
||||
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
|
||||
@Parameter(name = "taskId", description = "当前任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
|
||||
return success(taskService.getReturnTaskList(taskId));
|
||||
}
|
||||
|
||||
@PutMapping("/return")
|
||||
@Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
|
||||
taskService.returnTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/delegate")
|
||||
@Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮。和向前【加签】有点像,唯一区别是【委托】没有单独创立任务")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
|
||||
// TODO @海:, 后面要有空格
|
||||
taskService.delegateTask(reqVO,getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 委派流程任务的 Request VO")
|
||||
@Data
|
||||
public class BpmTaskDelegateReqVO {
|
||||
|
||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotEmpty(message = "任务编号不能为空")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "被委派人 ID 不能为空")
|
||||
private Long delegateUserId;
|
||||
|
||||
@Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅")
|
||||
@NotEmpty(message = "委派原因不能为空")
|
||||
private String reason;
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Schema(description = "管理后台 - 回退流程任务的 Request VO")
|
||||
@Data
|
||||
public class BpmTaskReturnReqVO {
|
||||
|
||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotEmpty(message = "任务编号不能为空")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "回退到的任务 Key 不能为空")
|
||||
private String targetDefinitionKey;
|
||||
|
||||
@Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
|
||||
@NotEmpty(message = "回退意见不能为空")
|
||||
private String reason;
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 流程任务的精简 Response VO")
|
||||
@Data
|
||||
public class BpmTaskSimpleRespVO {
|
||||
|
||||
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
|
||||
private String definitionKey;
|
||||
|
||||
@Schema(description = "任务名词", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批")
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
|||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskSimpleRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
|
||||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
@ -55,8 +57,8 @@ public interface BpmTaskConvert {
|
|||
}
|
||||
|
||||
default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks,
|
||||
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap,
|
||||
Map<Long, AdminUserRespDTO> userMap) {
|
||||
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap,
|
||||
Map<Long, AdminUserRespDTO> userMap) {
|
||||
return CollectionUtils.convertList(tasks, task -> {
|
||||
BpmTaskDonePageItemRespVO respVO = convert2(task);
|
||||
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
|
||||
|
@ -82,8 +84,8 @@ public interface BpmTaskConvert {
|
|||
BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
|
||||
|
||||
default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
|
||||
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance,
|
||||
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
|
||||
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance,
|
||||
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
|
||||
return CollectionUtils.convertList(tasks, task -> {
|
||||
BpmTaskRespVO respVO = convert3(task);
|
||||
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
|
||||
|
@ -113,29 +115,34 @@ public interface BpmTaskConvert {
|
|||
void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
|
||||
|
||||
@Mappings({@Mapping(source = "processInstance.id", target = "id"),
|
||||
@Mapping(source = "processInstance.name", target = "name"),
|
||||
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
|
||||
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
|
||||
@Mapping(source = "startUser.nickname", target = "startUserNickname")})
|
||||
@Mapping(source = "processInstance.name", target = "name"),
|
||||
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
|
||||
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
|
||||
@Mapping(source = "startUser.nickname", target = "startUserNickname")})
|
||||
BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance,
|
||||
AdminUserRespDTO startUser);
|
||||
AdminUserRespDTO startUser);
|
||||
|
||||
default BpmTaskExtDO convert2TaskExt(Task task) {
|
||||
BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId())
|
||||
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName())
|
||||
.setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId());
|
||||
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName())
|
||||
.setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId());
|
||||
taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime()));
|
||||
return taskExtDO;
|
||||
}
|
||||
|
||||
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
|
||||
Task task) {
|
||||
Task task) {
|
||||
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
|
||||
reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
|
||||
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
|
||||
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
|
||||
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
|
||||
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
|
||||
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
|
||||
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
|
||||
return reqDTO;
|
||||
}
|
||||
|
||||
default List<BpmTaskSimpleRespVO> convertList(List<? extends FlowElement> elementList) {
|
||||
return CollectionUtils.convertList(elementList, element -> new BpmTaskSimpleRespVO()
|
||||
.setName(element.getName())
|
||||
.setDefinitionKey(element.getId()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,4 +74,12 @@ public interface BpmModelService {
|
|||
*/
|
||||
BpmnModel getBpmnModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程定义编号对应的 BPMN Model
|
||||
*
|
||||
* @param processDefinitionId 流程定义编号
|
||||
* @return BPMN Model
|
||||
*/
|
||||
BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);
|
||||
|
||||
}
|
||||
|
|
|
@ -233,6 +233,11 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {
|
||||
return repositoryService.getBpmnModel(processDefinitionId);
|
||||
}
|
||||
|
||||
private void checkKeyNCName(String key) {
|
||||
if (!ValidationUtils.isXmlNCName(key)) {
|
||||
throw exception(MODEL_KEY_VALID);
|
||||
|
|
|
@ -5,7 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
|
|||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
|
||||
|
@ -200,7 +200,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
|||
BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
|
||||
BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
|
||||
// 对比字节变化
|
||||
if (!FlowableUtils.equals(oldModel, newModel)) {
|
||||
if (!BpmnModelUtils.equals(oldModel, newModel)) {
|
||||
return false;
|
||||
}
|
||||
// 最终发现都一致,则返回 true
|
||||
|
|
|
@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
|
||||
|
@ -114,7 +114,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
// 获得用户任务,只有用户任务才可以设置分配规则
|
||||
List<UserTask> userTasks = FlowableUtils.getBpmnModelElements(model, UserTask.class);
|
||||
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(model, UserTask.class);
|
||||
if (CollUtil.isEmpty(userTasks)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||
|
@ -23,7 +22,6 @@ public interface BpmTaskService {
|
|||
*
|
||||
* @param userId 用户编号
|
||||
* @param pageReqVO 分页请求
|
||||
*
|
||||
* @return 流程任务分页
|
||||
*/
|
||||
PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageReqVO);
|
||||
|
@ -33,7 +31,6 @@ public interface BpmTaskService {
|
|||
*
|
||||
* @param userId 用户编号
|
||||
* @param pageReqVO 分页请求
|
||||
*
|
||||
* @return 流程任务分页
|
||||
*/
|
||||
PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageReqVO);
|
||||
|
@ -42,19 +39,17 @@ public interface BpmTaskService {
|
|||
* 获得流程任务 Map
|
||||
*
|
||||
* @param processInstanceIds 流程实例的编号数组
|
||||
*
|
||||
* @return 流程任务 Map
|
||||
*/
|
||||
default Map<String, List<Task>> getTaskMapByProcessInstanceIds(List<String> processInstanceIds) {
|
||||
return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds),
|
||||
Task::getProcessInstanceId);
|
||||
Task::getProcessInstanceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程任务列表
|
||||
*
|
||||
* @param processInstanceIds 流程实例的编号数组
|
||||
*
|
||||
* @return 流程任务列表
|
||||
*/
|
||||
List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);
|
||||
|
@ -63,7 +58,6 @@ public interface BpmTaskService {
|
|||
* 获得指令流程实例的流程任务列表,包括所有状态的
|
||||
*
|
||||
* @param processInstanceId 流程实例的编号
|
||||
*
|
||||
* @return 流程任务列表
|
||||
*/
|
||||
List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
|
||||
|
@ -128,4 +122,29 @@ public interface BpmTaskService {
|
|||
*/
|
||||
void updateTaskExtAssign(Task task);
|
||||
|
||||
/**
|
||||
* 获取当前任务的可回退的流程集合
|
||||
*
|
||||
* @param taskId 当前的任务 ID
|
||||
* @return 可以回退的节点列表
|
||||
*/
|
||||
List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId);
|
||||
|
||||
/**
|
||||
* 将任务回退到指定的 targetDefinitionKey 位置
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 回退的任务key和当前所在的任务ID
|
||||
*/
|
||||
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
|
||||
|
||||
// TODO @海:userId 放前面
|
||||
/**
|
||||
* 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批
|
||||
*
|
||||
* @param reqVO 被委派人和被委派的任务编号理由参数
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +1,36 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
|
||||
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.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.DelegationState;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.flowable.task.api.TaskQuery;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
|
@ -32,6 +39,7 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
@ -39,8 +47,7 @@ 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.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
|
@ -57,23 +64,29 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
private TaskService taskService;
|
||||
@Resource
|
||||
private HistoryService historyService;
|
||||
@Resource
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
@Resource
|
||||
private BpmModelService bpmModelService;
|
||||
@Resource
|
||||
private BpmMessageService messageService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Resource
|
||||
private BpmTaskExtMapper taskExtMapper;
|
||||
@Resource
|
||||
private BpmMessageService messageService;
|
||||
|
||||
@Override
|
||||
public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
|
||||
// 查询待办任务
|
||||
TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId)) // 分配给自己
|
||||
.orderByTaskCreateTime().desc(); // 创建时间倒序
|
||||
.orderByTaskCreateTime().desc(); // 创建时间倒序
|
||||
if (StrUtil.isNotBlank(pageVO.getName())) {
|
||||
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
|
||||
}
|
||||
|
@ -91,21 +104,21 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
|
||||
// 获得 ProcessInstance Map
|
||||
Map<String, ProcessInstance> processInstanceMap =
|
||||
processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId));
|
||||
processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId));
|
||||
// 获得 User Map
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
|
||||
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
|
||||
// 拼接结果
|
||||
return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap),
|
||||
taskQuery.count());
|
||||
taskQuery.count());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
|
||||
// 查询已办任务
|
||||
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
|
||||
.taskAssignee(String.valueOf(userId)) // 分配给自己
|
||||
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
|
||||
.taskAssignee(String.valueOf(userId)) // 分配给自己
|
||||
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
|
||||
if (StrUtil.isNotBlank(pageVO.getName())) {
|
||||
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
|
||||
}
|
||||
|
@ -123,19 +136,19 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
|
||||
// 获得 TaskExtDO Map
|
||||
List<BpmTaskExtDO> bpmTaskExtDOs =
|
||||
taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
|
||||
taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
|
||||
Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
|
||||
// 获得 ProcessInstance Map
|
||||
Map<String, HistoricProcessInstance> historicProcessInstanceMap =
|
||||
processInstanceService.getHistoricProcessInstanceMap(
|
||||
convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
|
||||
processInstanceService.getHistoricProcessInstanceMap(
|
||||
convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
|
||||
// 获得 User Map
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
|
||||
convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
|
||||
// 拼接结果
|
||||
return new PageResult<>(
|
||||
BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
|
||||
taskQuery.count());
|
||||
BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
|
||||
taskQuery.count());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -176,27 +189,58 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
|
||||
// 校验任务存在
|
||||
Task task = checkTask(userId, reqVO.getId());
|
||||
// 校验流程实例存在
|
||||
// 1.1 校验任务存在
|
||||
Task task = validateTask(userId, reqVO.getId());
|
||||
// 1.2 校验流程实例存在
|
||||
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
|
||||
if (instance == null) {
|
||||
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 情况一:被委派的任务,不调用 complete 去完成任务
|
||||
if (DelegationState.PENDING.equals(task.getDelegationState())) {
|
||||
approveDelegateTask(reqVO, task);
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二:自己审批的任务,调用 complete 去完成任务
|
||||
// 完成任务,审批通过
|
||||
taskService.complete(task.getId(), instance.getProcessVariables());
|
||||
|
||||
// 更新任务拓展表为通过
|
||||
taskExtMapper.updateByTaskId(
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult())
|
||||
.setReason(reqVO.getReason()));
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult())
|
||||
.setReason(reqVO.getReason()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批被委派的任务
|
||||
*
|
||||
* @param reqVO 前端请求参数,包含当前任务ID,审批意见等
|
||||
* @param task 当前被审批的任务
|
||||
*/
|
||||
private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) {
|
||||
// 1. 添加审批意见
|
||||
AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId());
|
||||
AdminUserRespDTO sourceApproveUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner()));
|
||||
Assert.notNull(sourceApproveUser, "委派任务找不到原审批人,需要检查数据");
|
||||
String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(),
|
||||
sourceApproveUser.getNickname(), reqVO.getReason());
|
||||
taskService.addComment(reqVO.getId(), task.getProcessInstanceId(),
|
||||
BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment);
|
||||
|
||||
// 2.1 调用 resolveTask 完成任务。
|
||||
// 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee
|
||||
taskService.resolveTask(task.getId());
|
||||
// 2.2 更新任务拓展表为【处理中】
|
||||
taskExtMapper.updateByTaskId(
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())
|
||||
.setReason(reqVO.getReason()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
|
||||
Task task = checkTask(userId, reqVO.getId());
|
||||
Task task = validateTask(userId, reqVO.getId());
|
||||
// 校验流程实例存在
|
||||
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
|
||||
if (instance == null) {
|
||||
|
@ -208,14 +252,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
|
||||
// 更新任务拓展表为不通过
|
||||
taskExtMapper.updateByTaskId(
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult())
|
||||
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult())
|
||||
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO) {
|
||||
// 校验任务存在
|
||||
Task task = checkTask(userId, reqVO.getId());
|
||||
Task task = validateTask(userId, reqVO.getId());
|
||||
// 更新负责人
|
||||
updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId());
|
||||
}
|
||||
|
@ -231,13 +275,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
* @param userId 用户 id
|
||||
* @param taskId task id
|
||||
*/
|
||||
private Task checkTask(Long userId, String taskId) {
|
||||
private Task validateTask(Long userId, String taskId) {
|
||||
Task task = getTask(taskId);
|
||||
if (task == null) {
|
||||
throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
|
||||
throw exception(TASK_NOT_EXISTS);
|
||||
}
|
||||
if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
|
||||
throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
|
||||
throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
@ -245,7 +289,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
@Override
|
||||
public void createTaskExt(Task task) {
|
||||
BpmTaskExtDO taskExtDO =
|
||||
BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
|
||||
BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
|
||||
taskExtMapper.insert(taskExtDO);
|
||||
}
|
||||
|
||||
|
@ -293,27 +337,183 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
|||
@Override
|
||||
public void updateTaskExtAssign(Task task) {
|
||||
BpmTaskExtDO taskExtDO =
|
||||
new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
|
||||
new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
|
||||
taskExtMapper.updateByTaskId(taskExtDO);
|
||||
// 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
ProcessInstance processInstance =
|
||||
processInstanceService.getProcessInstance(task.getProcessInstanceId());
|
||||
processInstanceService.getProcessInstance(task.getProcessInstanceId());
|
||||
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
|
||||
messageService.sendMessageWhenTaskAssigned(
|
||||
BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Task getTask(String id) {
|
||||
return taskService.createTaskQuery().taskId(id).singleResult();
|
||||
Task task = taskService.createTaskQuery().taskId(id).singleResult();
|
||||
if (null == task) {
|
||||
throw exception(TASK_NOT_EXISTS);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private HistoricTaskInstance getHistoricTask(String id) {
|
||||
return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) {
|
||||
// 1. 校验当前任务 task 存在
|
||||
Task task = getTask(taskId);
|
||||
if (task == null) {
|
||||
throw exception(TASK_NOT_EXISTS);
|
||||
}
|
||||
// 根据流程定义获取流程模型信息
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
if (source == null) {
|
||||
throw exception(TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2.1 查询该任务的前置任务节点的 key 集合
|
||||
List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);
|
||||
if (CollUtil.isEmpty(previousUserList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
||||
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
|
||||
return BpmTaskConvert.INSTANCE.convertList(previousUserList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) {
|
||||
// 1.1 当前任务 task
|
||||
Task task = validateTask(userId, reqVO.getId());
|
||||
if (task.isSuspended()) {
|
||||
throw exception(TASK_IS_PENDING);
|
||||
}
|
||||
// 1.2 校验源头和目标节点的关系,并返回目标元素
|
||||
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());
|
||||
|
||||
// 2. 调用 flowable 框架的回退逻辑
|
||||
returnTask0(task, targetElement, reqVO);
|
||||
|
||||
// 3. 更新任务扩展表
|
||||
taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
|
||||
.setResult(BpmProcessInstanceResultEnum.BACK.getResult())
|
||||
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退流程节点时,校验目标任务节点是否可回退
|
||||
*
|
||||
* @param sourceKey 当前任务节点 Key
|
||||
* @param targetKey 目标任务节点 key
|
||||
* @param processDefinitionId 当前流程定义 ID
|
||||
* @return 目标任务节点元素
|
||||
*/
|
||||
private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
|
||||
// 1.1 获取流程模型信息
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
|
||||
// 1.3 获取当前任务节点元素
|
||||
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
|
||||
// 1.3 获取跳转的节点元素
|
||||
FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey);
|
||||
if (target == null) {
|
||||
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
|
||||
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
|
||||
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行回退逻辑
|
||||
*
|
||||
* @param currentTask 当前回退的任务
|
||||
* @param targetElement 需要回退到的目标任务
|
||||
* @param reqVO 前端参数封装
|
||||
*/
|
||||
public void returnTask0(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
|
||||
// 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤
|
||||
// 1.1 获取所有正常进行的任务节点 Key
|
||||
List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
|
||||
List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey);
|
||||
// 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回
|
||||
// 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F
|
||||
List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
|
||||
List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
|
||||
|
||||
// 2. 给当前要被回退的 task 数组,设置回退意见
|
||||
taskList.forEach(task -> {
|
||||
// 需要排除掉,不需要设置回退意见的任务
|
||||
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
|
||||
return;
|
||||
}
|
||||
taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
|
||||
BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());
|
||||
});
|
||||
|
||||
// 3. 执行驳回
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(currentTask.getProcessInstanceId())
|
||||
.moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
|
||||
reqVO.getTargetDefinitionKey()) // targetKey 跳转到的节点(1)
|
||||
.changeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId) {
|
||||
// 1.1 校验任务
|
||||
Task task = validateTaskCanDelegate(userId, reqVO);
|
||||
// 1.2 校验目标用户存在
|
||||
AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId());
|
||||
if (delegateUser == null) {
|
||||
throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 添加审批意见
|
||||
AdminUserRespDTO currentUser = adminUserApi.getUser(userId);
|
||||
String comment = StrUtil.format("[{}]将任务委派给[{}],委派理由为:{}", currentUser.getNickname(),
|
||||
delegateUser.getNickname(), reqVO.getReason());
|
||||
String taskId = reqVO.getId();
|
||||
// TODO 海:后面改;感觉 comment 应该 type 做个枚举;不和 result 耦合在一起;
|
||||
taskService.addComment(taskId, task.getProcessInstanceId(),
|
||||
BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment);
|
||||
|
||||
// 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)
|
||||
taskService.setOwner(taskId, task.getAssignee());
|
||||
// 3.2 执行委派,将任务委派给 receiveId
|
||||
taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());
|
||||
// 3.3 更新任务拓展表为【委派】
|
||||
taskExtMapper.updateByTaskId(
|
||||
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.DELEGATE.getResult())
|
||||
.setReason(reqVO.getReason()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验任务委派参数
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 任务编号,接收人ID
|
||||
* @return 当前任务信息
|
||||
*/
|
||||
private Task validateTaskCanDelegate(Long userId, BpmTaskDelegateReqVO reqVO) {
|
||||
// 校验任务
|
||||
Task task = validateTask(userId, reqVO.getId());
|
||||
// 校验当前审批人和被委派人不是同一人
|
||||
if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) {
|
||||
throw exception(TASK_DELEGATE_FAIL_USER_REPEAT);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,49 +9,49 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
|||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 参数配置 1001000000 ==========
|
||||
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1001000001, "参数配置不存在");
|
||||
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复");
|
||||
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置");
|
||||
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1001000004, "获取参数配置失败,原因:不允许获取不可见配置");
|
||||
// ========== 参数配置 1-001-000-000 ==========
|
||||
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
|
||||
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
|
||||
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
|
||||
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
|
||||
|
||||
// ========== 定时任务 1001001000 ==========
|
||||
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1001001000, "定时任务不存在");
|
||||
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1001001001, "定时任务的处理器已经存在");
|
||||
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1001001002, "只允许修改为开启或者关闭状态");
|
||||
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1001001003, "定时任务已经处于该状态,无需修改");
|
||||
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1001001004, "只有开启状态的任务,才可以修改");
|
||||
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1001001005, "CRON 表达式不正确");
|
||||
// ========== 定时任务 1-001-001-000 ==========
|
||||
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, "定时任务不存在");
|
||||
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, "定时任务的处理器已经存在");
|
||||
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, "只允许修改为开启或者关闭状态");
|
||||
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
|
||||
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
|
||||
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
|
||||
|
||||
// ========== API 错误日志 1001002000 ==========
|
||||
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在");
|
||||
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "API 错误日志已处理");
|
||||
// ========== API 错误日志 1-001-002-000 ==========
|
||||
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
|
||||
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
|
||||
|
||||
// ========= 文件相关 1001003000=================
|
||||
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1001003000, "文件路径已存在");
|
||||
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1001003001, "文件不存在");
|
||||
ErrorCode FILE_IS_EMPTY = new ErrorCode(1001003002, "文件为空");
|
||||
// ========= 文件相关 1-001-003-000 =================
|
||||
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
|
||||
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
|
||||
ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
|
||||
|
||||
// ========== 代码生成器 1001004000 ==========
|
||||
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1003001000, "表定义已经存在");
|
||||
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1003001001, "导入的表不存在");
|
||||
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1003001002, "导入的字段不存在");
|
||||
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1003001004, "表定义不存在");
|
||||
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1003001005, "字段义不存在");
|
||||
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1003001006, "同步的字段不存在");
|
||||
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1003001007, "同步失败,不存在改变");
|
||||
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1003001008, "数据库的表注释未填写");
|
||||
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1003001009, "数据库的表字段({})注释未填写");
|
||||
// ========== 代码生成器 1-001-004-000 ==========
|
||||
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_003_001_000, "表定义已经存在");
|
||||
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_003_001_001, "导入的表不存在");
|
||||
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_003_001_002, "导入的字段不存在");
|
||||
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_003_001_004, "表定义不存在");
|
||||
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_003_001_005, "字段义不存在");
|
||||
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_003_001_006, "同步的字段不存在");
|
||||
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_003_001_007, "同步失败,不存在改变");
|
||||
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_003_001_008, "数据库的表注释未填写");
|
||||
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_003_001_009, "数据库的表字段({})注释未填写");
|
||||
|
||||
// ========== 字典类型(测试)1001005000 ==========
|
||||
ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1001005000, "测试示例不存在");
|
||||
// ========== 字典类型(测试)1-001-005-000 ==========
|
||||
ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1_001_005_000, "测试示例不存在");
|
||||
|
||||
// ========== 文件配置 1001006000 ==========
|
||||
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1001006000, "文件配置不存在");
|
||||
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1001006001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
|
||||
// ========== 文件配置 1-001-006-000 ==========
|
||||
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
|
||||
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
|
||||
|
||||
// ========== 数据源配置 1001007000 ==========
|
||||
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1001007000, "数据源配置不存在");
|
||||
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1001007001, "数据源配置不正确,无法进行连接");
|
||||
// ========== 数据源配置 1-001-007-000 ==========
|
||||
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
|
||||
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
|
||||
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Mapper
|
||||
public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
|
||||
|
@ -21,7 +18,8 @@ public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
|
|||
.orderByDesc(FileConfigDO::getId));
|
||||
}
|
||||
|
||||
@Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
|
||||
Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
|
||||
default FileConfigDO selectByMaster() {
|
||||
return selectOne(FileConfigDO::getMaster, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package cn.iocoder.yudao.module.infra.service.file;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.file.core.client.FileClient;
|
||||
|
@ -17,22 +15,22 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigU
|
|||
import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Validator;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS;
|
||||
|
||||
|
@ -46,19 +44,29 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG
|
|||
@Slf4j
|
||||
public class FileConfigServiceImpl implements FileConfigService {
|
||||
|
||||
@Resource
|
||||
private FileClientFactory fileClientFactory;
|
||||
private static final Long CACHE_MASTER_ID = 0L;
|
||||
|
||||
/**
|
||||
* 文件配置的缓存
|
||||
* {@link FileClient} 缓存,通过它异步刷新 fileClientFactory
|
||||
*/
|
||||
@Getter
|
||||
private List<FileConfigDO> fileConfigCache;
|
||||
/**
|
||||
* Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的
|
||||
*/
|
||||
@Getter
|
||||
private FileClient masterFileClient;
|
||||
private final LoadingCache<Long, FileClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
|
||||
new CacheLoader<Long, FileClient>() {
|
||||
|
||||
@Override
|
||||
public FileClient load(Long id) {
|
||||
FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ?
|
||||
fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id);
|
||||
if (config != null) {
|
||||
fileClientFactory.createOrUpdateFileClient(id, config.getStorage(), config.getConfig());
|
||||
}
|
||||
return fileClientFactory.getFileClient(id);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Resource
|
||||
private FileClientFactory fileClientFactory;
|
||||
|
||||
@Resource
|
||||
private FileConfigMapper fileConfigMapper;
|
||||
|
@ -66,53 +74,12 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||
@Resource
|
||||
private Validator validator;
|
||||
|
||||
@PostConstruct
|
||||
public void initLocalCache() {
|
||||
// 第一步:查询数据
|
||||
List<FileConfigDO> configs = fileConfigMapper.selectList();
|
||||
log.info("[initLocalCache][缓存文件配置,数量为:{}]", configs.size());
|
||||
|
||||
// 第二步:构建缓存:创建或更新文件 Client
|
||||
configs.forEach(config -> {
|
||||
fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
|
||||
// 如果是 master,进行设置
|
||||
if (Boolean.TRUE.equals(config.getMaster())) {
|
||||
masterFileClient = fileClientFactory.getFileClient(config.getId());
|
||||
}
|
||||
});
|
||||
this.fileConfigCache = configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过定时任务轮询,刷新缓存
|
||||
*
|
||||
* 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
|
||||
*/
|
||||
@Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
|
||||
public void refreshLocalCache() {
|
||||
// 情况一:如果缓存里没有数据,则直接刷新缓存
|
||||
if (CollUtil.isEmpty(fileConfigCache)) {
|
||||
initLocalCache();
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
|
||||
LocalDateTime maxTime = CollectionUtils.getMaxValue(fileConfigCache, FileConfigDO::getUpdateTime);
|
||||
if (fileConfigMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
|
||||
initLocalCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createFileConfig(FileConfigCreateReqVO createReqVO) {
|
||||
// 插入
|
||||
FileConfigDO fileConfig = FileConfigConvert.INSTANCE.convert(createReqVO)
|
||||
.setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig()))
|
||||
.setMaster(false); // 默认非 master
|
||||
fileConfigMapper.insert(fileConfig);
|
||||
|
||||
// 刷新缓存
|
||||
initLocalCache();
|
||||
return fileConfig.getId();
|
||||
}
|
||||
|
||||
|
@ -125,8 +92,8 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||
.setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig()));
|
||||
fileConfigMapper.updateById(updateObj);
|
||||
|
||||
// 刷新缓存
|
||||
initLocalCache();
|
||||
// 清空缓存
|
||||
clearCache(config.getId(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -139,8 +106,8 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||
// 更新
|
||||
fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true));
|
||||
|
||||
// 刷新缓存
|
||||
initLocalCache();
|
||||
// 清空缓存
|
||||
clearCache(null, true);
|
||||
}
|
||||
|
||||
private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) {
|
||||
|
@ -164,8 +131,23 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||
// 删除
|
||||
fileConfigMapper.deleteById(id);
|
||||
|
||||
// 刷新缓存
|
||||
initLocalCache();
|
||||
// 清空缓存
|
||||
clearCache(id, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空指定文件配置
|
||||
*
|
||||
* @param id 配置编号
|
||||
* @param master 是否主配置
|
||||
*/
|
||||
private void clearCache(Long id, Boolean master) {
|
||||
if (id != null) {
|
||||
clientCache.invalidate(id);
|
||||
}
|
||||
if (Boolean.TRUE.equals(master)) {
|
||||
clientCache.invalidate(CACHE_MASTER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private FileConfigDO validateFileConfigExists(Long id) {
|
||||
|
@ -192,12 +174,17 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||
validateFileConfigExists(id);
|
||||
// 上传文件
|
||||
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
|
||||
return fileClientFactory.getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
|
||||
return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileClient getFileClient(Long id) {
|
||||
return fileClientFactory.getFileClient(id);
|
||||
return clientCache.getUnchecked(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileClient getMasterFileClient() {
|
||||
return clientCache.getUnchecked(CACHE_MASTER_ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,27 +3,27 @@ import { defHttp } from '@/utils/http/axios'
|
|||
|
||||
// 查询${table.classComment}列表
|
||||
export function get${simpleClassName}Page(params) {
|
||||
return defHttp.get({ url: '${baseURL}/page', params })
|
||||
return defHttp.get({ url: '${baseURL}/page', params })
|
||||
}
|
||||
|
||||
// 查询${table.classComment}详情
|
||||
export function get${simpleClassName}(id: number) {
|
||||
return defHttp.get({ url: '${baseURL}/get?id=' + id })
|
||||
return defHttp.get({ url: `${baseURL}/get?id=${id}` })
|
||||
}
|
||||
|
||||
// 新增${table.classComment}
|
||||
export function create${simpleClassName}(data) {
|
||||
return defHttp.post({ url: '${baseURL}/create', data })
|
||||
return defHttp.post({ url: '${baseURL}/create', data })
|
||||
}
|
||||
|
||||
// 修改${table.classComment}
|
||||
export function update${simpleClassName}(data) {
|
||||
return defHttp.put({ url: '${baseURL}/update', data })
|
||||
return defHttp.put({ url: '${baseURL}/update', data })
|
||||
}
|
||||
|
||||
// 删除${table.classComment}
|
||||
export function delete${simpleClassName}(id: number) {
|
||||
return defHttp.delete({ url: '${baseURL}/delete?id=' + id })
|
||||
return defHttp.delete({ url: `${baseURL}/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 导出${table.classComment} Excel
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
|
||||
import type { BasicColumn, FormSchema } from '@/components/Table'
|
||||
import { useRender } from '@/components/Table'
|
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
|
@ -92,13 +93,13 @@ export const createFormSchema: FormSchema[] = [
|
|||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
component: 'FileUpload',
|
||||
componentProps: {
|
||||
fileType: 'file',
|
||||
fileType: 'image',
|
||||
maxCount: 1,
|
||||
},
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
component: 'FileUpload',
|
||||
componentProps: {
|
||||
fileType: 'image',
|
||||
fileType: 'file',
|
||||
maxCount: 1,
|
||||
},
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
|
@ -132,6 +133,11 @@ export const createFormSchema: FormSchema[] = [
|
|||
},
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
#elseif($column.htmlType == "textarea")## 文本域
|
||||
component: 'InputTextArea',
|
||||
#end
|
||||
|
@ -154,7 +160,7 @@ export const updateFormSchema: FormSchema[] = [
|
|||
#set ($javaField = $column.javaField)
|
||||
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#set ($comment = $column.columnComment)
|
||||
#if (!$column.primaryKey)## 忽略主键,不用在表单里
|
||||
#if (!$column.primaryKey)## 忽略主键,不用在表单里
|
||||
{
|
||||
label: '${comment}',
|
||||
field: '${javaField}',
|
||||
|
@ -164,45 +170,57 @@ export const updateFormSchema: FormSchema[] = [
|
|||
#if ($column.htmlType == "input")
|
||||
component: 'Input',
|
||||
#elseif($column.htmlType == "imageUpload")## 图片上传
|
||||
component: 'Upload',
|
||||
component: 'FileUpload',
|
||||
componentProps: {
|
||||
fileType: 'image',
|
||||
maxCount: 1,
|
||||
},
|
||||
#elseif($column.htmlType == "fileUpload")## 文件上传
|
||||
component: 'Upload',
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器
|
||||
component: 'Editor',
|
||||
component: 'FileUpload',
|
||||
componentProps: {
|
||||
fileType: 'file',
|
||||
maxCount: 1,
|
||||
},
|
||||
#elseif($column.htmlType == "editor")## 文本编辑器component: 'Editor',
|
||||
#elseif($column.htmlType == "select")## 下拉框
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
#else##没数据字典
|
||||
options:[],
|
||||
options:[],
|
||||
#end
|
||||
},
|
||||
#elseif($column.htmlType == "checkbox")## 多选框
|
||||
component: 'Checkbox',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
#else##没数据字典
|
||||
options:[],
|
||||
options:[],
|
||||
#end
|
||||
},
|
||||
#elseif($column.htmlType == "radio")## 单选框
|
||||
component: 'RadioButtonGroup',
|
||||
componentProps: {
|
||||
#if ("" != $dictType)## 有数据字典
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
|
||||
#else##没数据字典
|
||||
options:[],
|
||||
options:[],
|
||||
#end
|
||||
},
|
||||
#elseif($column.htmlType == "datetime")## 时间框
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
#elseif($column.htmlType == "textarea")## 文本域
|
||||
component: 'InputTextArea',
|
||||
#end
|
||||
},
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
]
|
|
@ -9,9 +9,10 @@ import { create${simpleClassName}, get${simpleClassName}, update${simpleClassNam
|
|||
|
||||
defineOptions({ name: '${table.className}Modal' })
|
||||
|
||||
const emit = defineEmits(['success', 'register'])
|
||||
|
||||
const { t } = useI18n()
|
||||
const { createMessage } = useMessage()
|
||||
const emit = defineEmits(['success', 'register'])
|
||||
const isUpdate = ref(true)
|
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({
|
||||
|
@ -37,11 +38,11 @@ async function handleSubmit() {
|
|||
try {
|
||||
const values = await validate()
|
||||
setModalProps({ confirmLoading: true })
|
||||
if (unref(isUpdate)) {
|
||||
if (unref(isUpdate))
|
||||
await update${simpleClassName}(values)
|
||||
} else {
|
||||
else
|
||||
await create${simpleClassName}(values)
|
||||
}
|
||||
|
||||
closeModal()
|
||||
emit('success')
|
||||
createMessage.success(t('common.saveSuccessText'))
|
||||
|
@ -51,7 +52,7 @@ async function handleSubmit() {
|
|||
}
|
||||
</script>
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="isUpdate ? t('action.edit') : t('action.create')" @ok="handleSubmit">
|
||||
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts" setup>
|
||||
import ${simpleClassName}Modal from './${simpleClassName}Modal.vue'
|
||||
import ${ simpleClassName }Modal from './${simpleClassName}Modal.vue'
|
||||
import { columns, searchFormSchema } from './${classNameVar}.data'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import { useModal } from '@/components/Modal'
|
||||
import { IconEnum } from '@/enums/appEnum'
|
||||
import { BasicTable, useTable, TableAction } from '@/components/Table'
|
||||
import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${classNameVar}'
|
||||
import { delete${ simpleClassName }, export${ simpleClassName }, get${ simpleClassName } Page } from '@/api/${table.moduleName}/${classNameVar}'
|
||||
|
||||
defineOptions({ name: '${table.className}' })
|
||||
|
||||
|
@ -16,17 +16,17 @@ const [registerModal, { openModal }] = useModal()
|
|||
|
||||
const [registerTable, { getForm, reload }] = useTable({
|
||||
title: '${table.classComment}列表',
|
||||
api: get${simpleClassName}Page,
|
||||
columns,
|
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema },
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
actionColumn: {
|
||||
width: 140,
|
||||
title: t('common.action'),
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
},
|
||||
api: get${ simpleClassName }Page,
|
||||
columns,
|
||||
formConfig: { labelWidth: 120, schemas: searchFormSchema },
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
actionColumn: {
|
||||
width: 140,
|
||||
title: t('common.action'),
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
},
|
||||
})
|
||||
|
||||
function handleCreate() {
|
||||
|
@ -43,14 +43,14 @@ async function handleExport() {
|
|||
iconType: 'warning',
|
||||
content: t('common.exportMessage'),
|
||||
async onOk() {
|
||||
await export${simpleClassName}(getForm().getFieldsValue())
|
||||
await export${ simpleClassName } (getForm().getFieldsValue())
|
||||
createMessage.success(t('common.exportSuccessText'))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function handleDelete(record: Recordable) {
|
||||
await delete${simpleClassName}(record.id)
|
||||
await delete${ simpleClassName } (record.id)
|
||||
createMessage.success(t('common.delSuccessText'))
|
||||
reload()
|
||||
}
|
||||
|
|
|
@ -59,31 +59,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
@MockBean
|
||||
private FileClientFactory fileClientFactory;
|
||||
|
||||
@Test
|
||||
public void testInitLocalCache() {
|
||||
// mock 数据
|
||||
FileConfigDO configDO1 = randomFileConfigDO().setId(1L).setMaster(true);
|
||||
fileConfigMapper.insert(configDO1);
|
||||
FileConfigDO configDO2 = randomFileConfigDO().setId(2L).setMaster(false);
|
||||
fileConfigMapper.insert(configDO2);
|
||||
// mock fileClientFactory 获得 master
|
||||
FileClient masterFileClient = mock(FileClient.class);
|
||||
when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient);
|
||||
|
||||
// 调用
|
||||
fileConfigService.initLocalCache();
|
||||
// 断言 fileClientFactory 调用
|
||||
verify(fileClientFactory).createOrUpdateFileClient(eq(1L),
|
||||
eq(configDO1.getStorage()), eq(configDO1.getConfig()));
|
||||
verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
|
||||
eq(configDO2.getStorage()), eq(configDO2.getConfig()));
|
||||
assertSame(masterFileClient, fileConfigService.getMasterFileClient());
|
||||
// 断言 fileConfigCache 缓存
|
||||
assertEquals(2, fileConfigService.getFileConfigCache().size());
|
||||
assertEquals(configDO1, fileConfigService.getFileConfigCache().get(0));
|
||||
assertEquals(configDO2, fileConfigService.getFileConfigCache().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFileConfig_success() {
|
||||
// 准备参数
|
||||
|
@ -102,6 +77,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
assertFalse(fileConfig.getMaster());
|
||||
assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
|
||||
assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
|
||||
// 验证 cache
|
||||
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfigId));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -125,6 +102,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
assertPojoEquals(reqVO, fileConfig, "config");
|
||||
assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
|
||||
assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
|
||||
// 验证 cache
|
||||
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfig.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -149,6 +128,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
// 断言数据
|
||||
assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster());
|
||||
assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster());
|
||||
// 验证 cache
|
||||
assertNull(fileConfigService.getClientCache().getIfPresent(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -169,6 +150,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
fileConfigService.deleteFileConfig(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(fileConfigMapper.selectById(id));
|
||||
// 验证 cache
|
||||
assertNull(fileConfigService.getClientCache().getIfPresent(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -250,14 +233,38 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||
|
||||
@Test
|
||||
public void testGetFileClient() {
|
||||
// mock 数据
|
||||
FileConfigDO fileConfig = randomFileConfigDO().setMaster(false);
|
||||
fileConfigMapper.insert(fileConfig);
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
Long id = fileConfig.getId();
|
||||
// mock 获得 Client
|
||||
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
|
||||
when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient);
|
||||
|
||||
// 调用,并断言
|
||||
assertSame(fileClient, fileConfigService.getFileClient(id));
|
||||
// 断言缓存
|
||||
verify(fileClientFactory).createOrUpdateFileClient(eq(id), eq(fileConfig.getStorage()),
|
||||
eq(fileConfig.getConfig()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMasterFileClient() {
|
||||
// mock 数据
|
||||
FileConfigDO fileConfig = randomFileConfigDO().setMaster(true);
|
||||
fileConfigMapper.insert(fileConfig);
|
||||
// 准备参数
|
||||
Long id = fileConfig.getId();
|
||||
// mock 获得 Client
|
||||
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
|
||||
when(fileClientFactory.getFileClient(eq(0L))).thenReturn(fileClient);
|
||||
|
||||
// 调用,并断言
|
||||
assertSame(fileClient, fileConfigService.getMasterFileClient());
|
||||
// 断言缓存
|
||||
verify(fileClientFactory).createOrUpdateFileClient(eq(0L), eq(fileConfig.getStorage()),
|
||||
eq(fileConfig.getConfig()));
|
||||
}
|
||||
|
||||
private FileConfigDO randomFileConfigDO() {
|
||||
|
|
|
@ -139,4 +139,5 @@ public class FileServiceImplTest extends BaseDbUnitTest {
|
|||
// 断言
|
||||
assertSame(result, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<name>${project.artifactId}</name>
|
||||
|
||||
<description>
|
||||
商城大模块,由 product 商品、promotion 营销、trade 交易等组成
|
||||
商城大模块,由 product 商品、promotion 营销、trade 交易、statistics 统计等组成
|
||||
</description>
|
||||
<modules>
|
||||
<module>yudao-module-promotion-api</module>
|
||||
|
@ -24,6 +24,8 @@
|
|||
<module>yudao-module-product-biz</module>
|
||||
<module>yudao-module-trade-api</module>
|
||||
<module>yudao-module-trade-biz</module>
|
||||
<module>yudao-module-statistics-api</module>
|
||||
<module>yudao-module-statistics-biz</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -110,11 +110,6 @@ public class ProductSpuRespDTO {
|
|||
|
||||
// ========== 物流相关字段 =========
|
||||
|
||||
/**
|
||||
* 赠送积分
|
||||
*/
|
||||
private Integer giveIntegral;
|
||||
|
||||
/**
|
||||
* 物流配置模板编号
|
||||
*
|
||||
|
@ -122,6 +117,13 @@ public class ProductSpuRespDTO {
|
|||
*/
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
// ========== 营销相关字段 =========
|
||||
|
||||
/**
|
||||
* 赠送积分
|
||||
*/
|
||||
private Integer giveIntegral;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
|||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 商品分类相关 1008001000 ============
|
||||
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1008001001, "父分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
|
||||
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
|
||||
ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
|
||||
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1008001005, "类别下存在商品,无法删除");
|
||||
// ========== 商品分类相关 1-008-001-000 ============
|
||||
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_008_001_000, "商品分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_008_001_001, "父分类不存在");
|
||||
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_008_001_002, "父分类不能是二级分类");
|
||||
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_008_001_003, "存在子分类,无法删除");
|
||||
ErrorCode CATEGORY_DISABLED = new ErrorCode(1_008_001_004, "商品分类({})已禁用,无法使用");
|
||||
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1_008_001_005, "类别下存在商品,无法删除");
|
||||
|
||||
// ========== 商品品牌相关编号 1008002000 ==========
|
||||
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
|
||||
ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌已禁用");
|
||||
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
|
||||
// ========== 商品品牌相关编号 1-008-002-000 ==========
|
||||
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1_008_002_000, "品牌不存在");
|
||||
ErrorCode BRAND_DISABLED = new ErrorCode(1_008_002_001, "品牌已禁用");
|
||||
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1_008_002_002, "品牌名称已存在");
|
||||
|
||||
// ========== 商品属性项 1008003000 ==========
|
||||
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "属性项不存在");
|
||||
ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "属性项的名称已存在");
|
||||
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1008003002, "属性项下存在属性值,无法删除");
|
||||
// ========== 商品属性项 1-008-003-000 ==========
|
||||
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1_008_003_000, "属性项不存在");
|
||||
ErrorCode PROPERTY_EXISTS = new ErrorCode(1_008_003_001, "属性项的名称已存在");
|
||||
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1_008_003_002, "属性项下存在属性值,无法删除");
|
||||
|
||||
// ========== 商品属性值 1008004000 ==========
|
||||
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "属性值不存在");
|
||||
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1008004001, "属性值的名称已存在");
|
||||
// ========== 商品属性值 1-008-004-000 ==========
|
||||
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1_008_004_000, "属性值不存在");
|
||||
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1_008_004_001, "属性值的名称已存在");
|
||||
|
||||
// ========== 商品 SPU 1008005000 ==========
|
||||
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在");
|
||||
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
|
||||
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
|
||||
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1008005003, "商品 SPU 不处于回收站状态");
|
||||
// ========== 商品 SPU 1-008-005-000 ==========
|
||||
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在");
|
||||
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
|
||||
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_002, "商品 SPU 不处于上架状态");
|
||||
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_003, "商品 SPU 不处于回收站状态");
|
||||
|
||||
// ========== 商品 SKU 1008006000 ==========
|
||||
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");
|
||||
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复");
|
||||
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU,其属性项必须一致");
|
||||
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU,必须不重复");
|
||||
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
|
||||
// ========== 商品 SKU 1-008-006-000 ==========
|
||||
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在");
|
||||
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1_008_006_001, "商品 SKU 的属性组合存在重复");
|
||||
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1_008_006_002, "一个 SPU 下的每个 SKU,其属性项必须一致");
|
||||
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1_008_006_003, "一个 SPU 下的每个 SKU,必须不重复");
|
||||
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_008_006_004, "商品 SKU 库存不足");
|
||||
|
||||
// ========== 商品 评价 1008007000 ==========
|
||||
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品评价不存在");
|
||||
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1008007001, "订单的商品评价已存在");
|
||||
// ========== 商品 评价 1-008-007-000 ==========
|
||||
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, "商品评价不存在");
|
||||
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1_008_007_001, "订单的商品评价已存在");
|
||||
|
||||
// ========== 商品 收藏 1008008000 ==========
|
||||
ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
|
||||
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1008008001, "商品收藏不存在");
|
||||
// ========== 商品 收藏 1-008-008-000 ==========
|
||||
ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, "该商品已经被收藏");
|
||||
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, "商品收藏不存在");
|
||||
|
||||
}
|
||||
|
|
|
@ -140,4 +140,5 @@ public class AppProductSpuController {
|
|||
return price - newPrice;
|
||||
}
|
||||
|
||||
// TODO 芋艿:商品的浏览记录;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
|
|||
import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
|
||||
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
|
||||
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
|
||||
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
@ -48,8 +47,6 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
|
|||
@Lazy
|
||||
private ProductCommentServiceImpl productCommentService;
|
||||
|
||||
@MockBean
|
||||
private TradeOrderApi tradeOrderApi;
|
||||
@MockBean
|
||||
private ProductSpuService productSpuService;
|
||||
@MockBean
|
||||
|
|
|
@ -68,7 +68,7 @@ public class RewardActivityMatchRespDTO {
|
|||
*/
|
||||
private List<Long> couponIds;
|
||||
/**
|
||||
* 赠送的优惠卷数量的数组
|
||||
* 赠送的优惠券数量的数组
|
||||
*/
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
|
|
|
@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
|||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 促销活动相关 1013001000 ============
|
||||
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1013001000, "限时折扣活动不存在");
|
||||
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013001001, "存在商品参加了其它限时折扣活动");
|
||||
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013001002, "限时折扣活动已关闭,不能修改");
|
||||
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013001003, "限时折扣活动未关闭,不能删除");
|
||||
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013001004, "限时折扣活动已关闭,不能重复关闭");
|
||||
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013001005, "限时折扣活动已结束,不能关闭");
|
||||
// ========== 促销活动相关 1-013-001-000 ============
|
||||
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_001_000, "限时折扣活动不存在");
|
||||
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_001_001, "存在商品参加了其它限时折扣活动");
|
||||
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
|
||||
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
|
||||
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭");
|
||||
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_001_005, "限时折扣活动已结束,不能关闭");
|
||||
|
||||
// ========== Banner 相关 1013002000 ============
|
||||
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1013002000, "Banner 不存在");
|
||||
// ========== Banner 相关 1-013-002-000 ============
|
||||
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
|
||||
|
||||
// ========== Coupon 相关 1013003000 ============
|
||||
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1013003000, "优惠劵没有可使用的商品!");
|
||||
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1013003001, "所结算的商品中未满足使用的金额");
|
||||
// ========== Coupon 相关 1-013-003-000 ============
|
||||
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1_013_003_000, "优惠劵没有可使用的商品!");
|
||||
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1_013_003_001, "所结算的商品中未满足使用的金额");
|
||||
|
||||
// ========== 优惠劵模板 1013004000 ==========
|
||||
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1013004000, "优惠劵模板不存在");
|
||||
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1013004001, "发放数量不能小于已领取数量({})");
|
||||
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1013004002, "当前剩余数量不够领取");
|
||||
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1013004003, "用户已领取过此优惠券");
|
||||
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1013004004, "优惠券已过期");
|
||||
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1013004005, "领取方式不正确");
|
||||
// ========== 优惠劵模板 1-013-004-000 ==========
|
||||
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, "优惠劵模板不存在");
|
||||
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1_013_004_001, "发放数量不能小于已领取数量({})");
|
||||
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1_013_004_002, "当前剩余数量不够领取");
|
||||
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1_013_004_003, "用户已领取过此优惠券");
|
||||
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1_013_004_004, "优惠券已过期");
|
||||
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1_013_004_005, "领取方式不正确");
|
||||
|
||||
// ========== 优惠劵 1013005000 ==========
|
||||
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1013005000, "优惠券不存在");
|
||||
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1013005001, "回收优惠劵失败,优惠劵已被使用");
|
||||
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1013005002, "优惠劵不处于待使用状态");
|
||||
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1013005003, "优惠券不在使用时间范围内");
|
||||
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1013005004, "优惠劵不是已使用状态");
|
||||
// ========== 优惠劵 1-013-005-000 ==========
|
||||
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1_013_005_000, "优惠券不存在");
|
||||
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1_013_005_001, "回收优惠劵失败,优惠劵已被使用");
|
||||
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1_013_005_002, "优惠劵不处于待使用状态");
|
||||
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1_013_005_003, "优惠券不在使用时间范围内");
|
||||
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1_013_005_004, "优惠劵不是已使用状态");
|
||||
|
||||
// ========== 满减送活动 1013006000 ==========
|
||||
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1013006000, "满减送活动不存在");
|
||||
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013006001, "存在商品参加了其它满减送活动");
|
||||
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013006002, "满减送活动已关闭,不能修改");
|
||||
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013006003, "满减送活动未关闭,不能删除");
|
||||
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭");
|
||||
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭");
|
||||
// ========== 满减送活动 1-013-006-000 ==========
|
||||
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_006_000, "满减送活动不存在");
|
||||
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_006_001, "存在商品参加了其它满减送活动");
|
||||
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_002, "满减送活动已关闭,不能修改");
|
||||
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
|
||||
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
|
||||
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_006_005, "满减送活动已结束,不能关闭");
|
||||
|
||||
// ========== TODO 空着 1013007000 ============
|
||||
// ========== TODO 空着 1-013-007-000 ============
|
||||
|
||||
// ========== 秒杀活动 1013008000 ==========
|
||||
// ========== 秒杀活动 1-013-008-000 ==========
|
||||
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
|
||||
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
|
||||
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
|
||||
|
@ -57,19 +57,19 @@ public interface ErrorCodeConstants {
|
|||
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
|
||||
ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
|
||||
|
||||
// ========== 秒杀时段 1013009000 ==========
|
||||
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
|
||||
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1013009001, "秒杀时段冲突");
|
||||
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1013009004, "秒杀时段已关闭");
|
||||
// ========== 秒杀时段 1-013-009-000 ==========
|
||||
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, "秒杀时段不存在");
|
||||
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1_013_009_001, "秒杀时段冲突");
|
||||
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1_013_009_004, "秒杀时段已关闭");
|
||||
|
||||
// ========== 拼团活动 1013010000 ==========
|
||||
// ========== 拼团活动 1-013-010-000 ==========
|
||||
ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在");
|
||||
ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
|
||||
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
|
||||
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
|
||||
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010004, "拼团失败,原因:拼团活动已关闭");
|
||||
|
||||
// ========== 拼团记录 1013011000 ==========
|
||||
// ========== 拼团记录 1-013-011-000 ==========
|
||||
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013011000, "拼团不存在");
|
||||
ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013011001, "拼团失败,已参与过该拼团");
|
||||
ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在");
|
||||
|
@ -79,17 +79,17 @@ public interface ErrorCodeConstants {
|
|||
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,原因:单次限购超出");
|
||||
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:超出总购买次数");
|
||||
|
||||
// ========== 砍价活动 1013012000 ==========
|
||||
// ========== 砍价活动 1-013-012-000 ==========
|
||||
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在");
|
||||
ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
|
||||
ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
|
||||
ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
|
||||
ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
|
||||
|
||||
// ========== 砍价记录 1013013000 ==========
|
||||
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");
|
||||
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1013013001, "砍价失败,已参与过该砍价");
|
||||
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013013002, "砍价失败,父砍价不存在");
|
||||
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1013013003, "砍价失败,砍价人数已满");
|
||||
// ========== 砍价记录 1-013-013-000 ==========
|
||||
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1_013_013_000, "砍价记录不存在");
|
||||
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1_013_013_001, "砍价失败,已参与过该砍价");
|
||||
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_013_002, "砍价失败,父砍价不存在");
|
||||
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1_013_013_003, "砍价失败,砍价人数已满");
|
||||
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import java.util.Arrays;
|
|||
@AllArgsConstructor
|
||||
public enum PromotionProductScopeEnum implements IntArrayValuable {
|
||||
|
||||
ALL(1, "通用卷"), // 全部商品
|
||||
SPU(2, "商品卷"), // 指定商品
|
||||
CATEGORY(3, "品类卷"), // 指定商品
|
||||
ALL(1, "通用券"), // 全部商品
|
||||
SPU(2, "商品券"), // 指定商品
|
||||
CATEGORY(3, "品类券"), // 指定品类
|
||||
;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
|
||||
|
|
|
@ -45,6 +45,10 @@
|
|||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -8,6 +10,7 @@ import lombok.ToString;
|
|||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
|
@ -21,6 +24,7 @@ public class CouponPageReqVO extends PageParam {
|
|||
private Long templateId;
|
||||
|
||||
@Schema(description = "优惠码状态", example = "1")
|
||||
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
|
@ -30,4 +34,7 @@ public class CouponPageReqVO extends PageParam {
|
|||
@Schema(description = "用户昵称", example = "芋艿")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Collection<Long> userIds;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
@ -33,8 +34,15 @@ public class CouponTemplatePageReqVO extends PageParam {
|
|||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
@Schema(description = "可以领取的类型", example = "[1,2, 3]")
|
||||
@Schema(description = "可以领取的类型", example = "[1, 2, 3]")
|
||||
@InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
|
||||
private List<Integer> canTakeTypes;
|
||||
|
||||
@Schema(description = "商品范围", example = "1")
|
||||
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
|
||||
private Integer productScope;
|
||||
|
||||
@Schema(description = "商品范围编号", example = "1")
|
||||
private Long productScopeValue;
|
||||
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class RewardActivityBaseVO {
|
|||
@Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3")
|
||||
private List<Long> couponIds;
|
||||
|
||||
@Schema(description = "赠送的优惠卷数量的数组", example = "1,2,3")
|
||||
@Schema(description = "赠送的优惠券数量的数组", example = "1,2,3")
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
@AssertTrue(message = "优惠劵和数量必须一一对应")
|
||||
|
|
|
@ -9,41 +9,41 @@ import java.time.LocalDateTime;
|
|||
@Data
|
||||
public class AppArticleRespVO {
|
||||
|
||||
@Schema(description = "文章编号", required = true, example = "1")
|
||||
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "文章标题", required = true, example = "芋道源码 - 促销模块")
|
||||
@Schema(description = "文章标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码 - 促销模块")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文章作者", required = true, example = "芋道源码")
|
||||
@Schema(description = "文章作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
|
||||
private String author;
|
||||
|
||||
@Schema(description = "分类编号", required = true, example = "2048")
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "图文封面", required = true, example = "https://www.iocoder.cn/1.png")
|
||||
@Schema(description = "图文封面", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "文章简介", required = true, example = "我是简介")
|
||||
@Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "文章内容", required = true, example = "我是详细")
|
||||
@Schema(description = "文章内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是详细")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "发布时间", required = true)
|
||||
@Schema(description = "发布时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "浏览量", required = true, example = "1024")
|
||||
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer browseCount;
|
||||
|
||||
@Schema(description = "关联的商品 SPU 编号", example = "1024")
|
||||
private Long spuId;
|
||||
|
||||
// TODO 芋艿:下面 2 个字段,后端要存储,前端不用返回;
|
||||
// @Schema(description = "是否热卖推荐", required = true, example = "true")
|
||||
// @Schema(description = "是否热卖推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
// private Boolean recommendHot;
|
||||
//
|
||||
// @Schema(description = "是否 Banner 推荐", required = true, example = "true")
|
||||
// @Schema(description = "是否 Banner 推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
// private Boolean recommendBanner;
|
||||
|
||||
}
|
||||
|
|
|
@ -7,20 +7,20 @@ import lombok.Data;
|
|||
@Data
|
||||
public class AppArticleCategoryRespVO {
|
||||
|
||||
@Schema(description = "分类编号", required = true, example = "1")
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分类名称", required = true, example = "技术")
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "分类图标", required = true, example = "https://www.iocoder.cn/1.png")
|
||||
@Schema(description = "分类图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
// TODO 芋艿:下面 2 个字段,后端要存储,前端不用返回;
|
||||
// @Schema(description = "状态", required = true, example = "1")
|
||||
// @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
// private Integer status;
|
||||
//
|
||||
// @Schema(description = "排序", required = true, example = "1024")
|
||||
// @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
// private Integer sort;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,46 +9,46 @@ import java.time.LocalDateTime;
|
|||
@Data
|
||||
public class AppBargainActivityDetailRespVO {
|
||||
|
||||
@Schema(description = "砍价活动编号", required = true, example = "1024")
|
||||
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
|
||||
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "活动开始时间", required = true)
|
||||
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "活动结束时间", required = true)
|
||||
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品价格,单位:分", required = true, example = "100")
|
||||
@Schema(description = "商品价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品描述", required = true, example = "我要吃西红柿")
|
||||
@Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要吃西红柿")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "砍价库存", required = true, example = "512")
|
||||
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "商品图片", required = true, example = "4096") // 从 SPU 的 picUrl 读取
|
||||
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // 从 SPU 的 picUrl 读取
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // 从 SPU 的 marketPrice 读取
|
||||
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // 从 SPU 的 marketPrice 读取
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "商品单位", required = true, example = "个") // 从 SPU 的 unit 读取,然后转换
|
||||
@Schema(description = "商品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "个") // 从 SPU 的 unit 读取,然后转换
|
||||
private String unitName;
|
||||
|
||||
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
|
||||
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer bargainPrice;
|
||||
|
||||
@Schema(description = "砍价成功数量", required = true, example = "100")
|
||||
@Schema(description = "砍价成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer successCount;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,34 +9,34 @@ import java.time.LocalDateTime;
|
|||
@Data
|
||||
public class AppBargainActivityRespVO {
|
||||
|
||||
@Schema(description = "砍价活动编号", required = true, example = "1024")
|
||||
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
|
||||
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "活动开始时间", required = true)
|
||||
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "活动结束时间", required = true)
|
||||
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
|
||||
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "砍价库存", required = true, example = "512")
|
||||
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "商品图片", required = true, example = "4096") // 从 SPU 的 picUrl 读取
|
||||
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // 从 SPU 的 picUrl 读取
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // 从 SPU 的 marketPrice 读取
|
||||
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // 从 SPU 的 marketPrice 读取
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
|
||||
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Integer bargainPrice;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
|
|||
@Data
|
||||
public class AppBargainHelpCreateReqVO {
|
||||
|
||||
@Schema(description = "砍价记录编号", required = true, example = "1024")
|
||||
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "砍价记录编号不能为空")
|
||||
private Long recordId;
|
||||
|
||||
|
|
|
@ -9,16 +9,16 @@ import java.time.LocalDateTime;
|
|||
@Data
|
||||
public class AppBargainHelpRespVO {
|
||||
|
||||
@Schema(description = "助力用户的昵称", required = true, example = "1024")
|
||||
@Schema(description = "助力用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "助力用户的头像", required = true, example = "1024")
|
||||
@Schema(description = "助力用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "助力用户的砍价金额", required = true, example = "1024")
|
||||
@Schema(description = "助力用户的砍价金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer reducePrice;
|
||||
|
||||
@Schema(description = "创建时间", required = true, example = "1024")
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
|
|||
@Data
|
||||
public class AppBargainRecordCreateReqVO {
|
||||
|
||||
@Schema(description = "砍价活动编号", required = true, example = "1024")
|
||||
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "砍价活动编号不能为空")
|
||||
private Long activityId;
|
||||
|
||||
|
|
|
@ -9,23 +9,23 @@ import java.util.List;
|
|||
@Data
|
||||
public class AppBargainRecordSummaryRespVO {
|
||||
|
||||
@Schema(description = "砍价用户数量", required = true, example = "1024")
|
||||
@Schema(description = "砍价用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer userCount;
|
||||
|
||||
@Schema(description = "成功砍价的记录", required = true) // 只返回最近的 7 个
|
||||
@Schema(description = "成功砍价的记录", requiredMode = Schema.RequiredMode.REQUIRED) // 只返回最近的 7 个
|
||||
private List<Record> successRecords;
|
||||
|
||||
@Schema(description = "成功砍价记录")
|
||||
@Data
|
||||
public static class Record {
|
||||
|
||||
@Schema(description = "用户昵称", required = true, example = "王**")
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王**")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", required = true, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "活动名称", required = true, example = "天蚕土豆")
|
||||
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "天蚕土豆")
|
||||
private String activityName;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
package cn.iocoder.yudao.module.promotion.controller.app.coupon;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.*;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
@ -31,73 +34,42 @@ public class AppCouponController {
|
|||
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
@Resource
|
||||
private CouponTemplateService couponTemplateService;
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@PostMapping("/take")
|
||||
@Operation(summary = "领取优惠劵")
|
||||
public CommonResult<Long> takeCoupon(@RequestBody AppCouponTemplatePageReqVO pageReqVO) {
|
||||
return success(1L);
|
||||
@Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
|
||||
// 领取
|
||||
Long userId = getLoginUserId();
|
||||
couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
|
||||
|
||||
// 检查是否可以继续领取
|
||||
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
|
||||
boolean canTakeAgain = true;
|
||||
if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
|
||||
// TODO @疯狂:要不要搞个 getTakeCount 方法?
|
||||
Integer takeCount = MapUtil.getInt(couponService.getTakeCountMapByTemplateIds(
|
||||
Collections.singleton(reqVO.getTemplateId()), userId), reqVO.getTemplateId(), 0);
|
||||
canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
|
||||
}
|
||||
return success(canTakeAgain);
|
||||
}
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/match-list")
|
||||
@Operation(summary = "获得匹配指定商品的优惠劵列表")
|
||||
public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
|
||||
List<AppCouponMatchRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponMatchRespVO vo = new AppCouponMatchRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setMatch(random.nextBoolean());
|
||||
if (!vo.getMatch()) {
|
||||
vo.setDescription("不符合条件噢");
|
||||
}
|
||||
list.add(vo);
|
||||
}
|
||||
return success(list);
|
||||
// todo: 优化:优惠金额倒序
|
||||
return success(CouponConvert.INSTANCE.convertList(couponService.getMatchCouponList(getLoginUserId(), matchReqVO)));
|
||||
}
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "优惠劵列表", description = "我的优惠劵")
|
||||
public CommonResult<PageResult<AppCouponRespVO>> takeCoupon(AppCouponPageReqVO pageReqVO) {
|
||||
List<AppCouponRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponRespVO vo = new AppCouponRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setStatus(pageReqVO.getStatus());
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
list.add(vo);
|
||||
}
|
||||
return success(new PageResult<>(list, 20L));
|
||||
PageResult<CouponDO> pageResult = couponService.getCouponPage(
|
||||
CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));
|
||||
return success(CouponConvert.INSTANCE.convertAppPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/get-unused-count")
|
||||
|
|
|
@ -1,27 +1,32 @@
|
|||
package cn.iocoder.yudao.module.promotion.controller.app.coupon;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 App - 优惠劵模板")
|
||||
@RestController
|
||||
|
@ -31,84 +36,54 @@ public class AppCouponTemplateController {
|
|||
|
||||
@Resource
|
||||
private CouponTemplateService couponTemplateService;
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得优惠劵模版列表")
|
||||
@Parameters({
|
||||
@Parameter(name = "spuId", description = "商品 SPU 编号"), // 目前主要给商品详情使用
|
||||
@Parameter(name = "useType", description = "使用类型"),
|
||||
@Parameter(name = "count", description = "数量", required = true)
|
||||
})
|
||||
public CommonResult<List<AppCouponTemplateRespVO>> getCouponTemplateList(@RequestParam(value = "spuId", required = false) Long spuId,
|
||||
@RequestParam(value = "useType", required = false) Integer useType,
|
||||
@RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {
|
||||
List<AppCouponTemplateRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setTakeLimitCount(random.nextInt(10) + 1);
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidityType(random.nextInt(2) + 1);
|
||||
if (vo.getValidityType() == 1) {
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
} else {
|
||||
vo.setFixedStartTerm(random.nextInt(10));
|
||||
vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
|
||||
}
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setTakeStatus(random.nextBoolean());
|
||||
list.add(vo);
|
||||
}
|
||||
return success(list);
|
||||
}
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
|
||||
// TODO 芋艿:待实现;和 getCouponTemplateList 类似
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得优惠劵模版分页")
|
||||
public CommonResult<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
|
||||
List<AppCouponTemplateRespVO> list = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
AppCouponTemplateRespVO vo = new AppCouponTemplateRespVO();
|
||||
vo.setId(i + 1L);
|
||||
vo.setName("优惠劵" + (i + 1));
|
||||
vo.setTakeLimitCount(random.nextInt(10) + 1);
|
||||
vo.setUsePrice(random.nextInt(100) * 100);
|
||||
vo.setValidityType(random.nextInt(2) + 1);
|
||||
if (vo.getValidityType() == 1) {
|
||||
vo.setValidStartTime(LocalDateTime.now().plusDays(random.nextInt(10)));
|
||||
vo.setValidEndTime(LocalDateTime.now().plusDays(random.nextInt(20) + 10));
|
||||
} else {
|
||||
vo.setFixedStartTerm(random.nextInt(10));
|
||||
vo.setFixedEndTerm(random.nextInt(10) + vo.getFixedStartTerm() + 1);
|
||||
}
|
||||
vo.setDiscountType(random.nextInt(2) + 1);
|
||||
if (vo.getDiscountType() == 1) {
|
||||
vo.setDiscountPercent(null);
|
||||
vo.setDiscountPrice(random.nextInt(50) * 100);
|
||||
vo.setDiscountLimitPrice(null);
|
||||
} else {
|
||||
vo.setDiscountPercent(random.nextInt(90) + 10);
|
||||
vo.setDiscountPrice(null);
|
||||
vo.setDiscountLimitPrice(random.nextInt(200) * 100);
|
||||
}
|
||||
vo.setTakeStatus(random.nextBoolean());
|
||||
list.add(vo);
|
||||
// 1.1 处理查询条件:商品范围编号
|
||||
Long productScopeValue = getProductScopeValue(pageReqVO);
|
||||
// 1.2 处理查询条件:领取方式 = 直接领取
|
||||
List<Integer> canTakeTypes = Collections.singletonList(CouponTakeTypeEnum.USER.getValue());
|
||||
|
||||
// 2. 分页查询
|
||||
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
|
||||
CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
|
||||
|
||||
// 3.1 领取数量
|
||||
Map<Long, Integer> couponTakeCountMap = new HashMap<>(0);
|
||||
Long userId = getLoginUserId();
|
||||
if (userId != null) {
|
||||
List<Long> templateIds = convertList(pageResult.getList(), CouponTemplateDO::getId,
|
||||
t -> ObjUtil.notEqual(t.getTakeLimitCount(), -1)); // 只查有设置“每人限领个数”的
|
||||
couponTakeCountMap = couponService.getTakeCountMapByTemplateIds(templateIds, userId);
|
||||
}
|
||||
return success(new PageResult<>(list, 20L));
|
||||
// 3.2 拼接返回
|
||||
return success(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, couponTakeCountMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得分页查询的商品范围
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 商品范围
|
||||
*/
|
||||
private Long getProductScopeValue(AppCouponTemplatePageReqVO pageReqVO) {
|
||||
// 通用券:清除商品范围
|
||||
if (pageReqVO.getProductScope() == null || ObjectUtils.equalsAny(pageReqVO.getProductScope(), PromotionProductScopeEnum.ALL.getScope(), null)) {
|
||||
return null;
|
||||
}
|
||||
// 品类券:查询商品的品类
|
||||
if (Objects.equals(pageReqVO.getProductScope(), PromotionProductScopeEnum.CATEGORY.getScope()) && pageReqVO.getSpuId() != null) {
|
||||
return Optional.ofNullable(productSpuApi.getSpu(pageReqVO.getSpuId()))
|
||||
.map(ProductSpuRespDTO::getCategoryId).orElse(null);
|
||||
}
|
||||
// 商品卷:直接返回
|
||||
return pageReqVO.getSpuId();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,15 +15,15 @@ public class AppCouponMatchReqVO {
|
|||
@NotNull(message = "商品金额不能为空")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "商品 SPU 编号的数组", required = true, example = "[1, 2]")
|
||||
@Schema(description = "商品 SPU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "商品 SPU 编号不能为空")
|
||||
private List<Long> spuIds;
|
||||
|
||||
@Schema(description = "商品 SKU 编号的数组", required = true, example = "[1, 2]")
|
||||
@Schema(description = "商品 SKU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "商品 SKU 编号不能为空")
|
||||
private List<Long> skuIds;
|
||||
|
||||
@Schema(description = "分类编号的数组", required = true, example = "[10, 20]")
|
||||
@Schema(description = "分类编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[10, 20]")
|
||||
@NotEmpty(message = "分类编号不能为空")
|
||||
private List<Long> categoryIds;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -13,6 +15,7 @@ import lombok.ToString;
|
|||
public class AppCouponPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "优惠劵状态", example = "1")
|
||||
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -12,8 +14,11 @@ import lombok.ToString;
|
|||
@ToString(callSuper = true)
|
||||
public class AppCouponTemplatePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "使用类型", example = "1")
|
||||
// TODO 芋艿:这里要限制下枚举的使用
|
||||
private Integer useType;
|
||||
@Schema(description = "商品范围", example = "1")
|
||||
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
|
||||
private Integer productScope;
|
||||
|
||||
@Schema(description = "商品标号", example = "1")
|
||||
private Long spuId;
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.promotion.convert.coupon;
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
|
@ -11,6 +15,8 @@ import org.mapstruct.Mapper;
|
|||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 优惠劵 Convert
|
||||
|
@ -49,4 +55,11 @@ public interface CouponConvert {
|
|||
}
|
||||
return couponDO;
|
||||
}
|
||||
|
||||
CouponPageReqVO convert(AppCouponPageReqVO pageReqVO, Collection<Long> userIds);
|
||||
|
||||
PageResult<AppCouponRespVO> convertAppPage(PageResult<CouponDO> pageResult);
|
||||
|
||||
List<AppCouponMatchRespVO> convertList(List<CouponDO> list);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
package cn.iocoder.yudao.module.promotion.convert.coupon;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.template.AppCouponTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 优惠劵模板 Convert
|
||||
*
|
||||
|
@ -26,4 +33,25 @@ public interface CouponTemplateConvert {
|
|||
|
||||
PageResult<CouponTemplateRespVO> convertPage(PageResult<CouponTemplateDO> page);
|
||||
|
||||
CouponTemplatePageReqVO convert(AppCouponTemplatePageReqVO pageReqVO, List<Integer> canTakeTypes, Integer productScope, Long productScopeValue);
|
||||
|
||||
PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult);
|
||||
|
||||
default PageResult<AppCouponTemplateRespVO> convertAppPage(PageResult<CouponTemplateDO> pageResult, Map<Long, Integer> couponTakeCountMap) {
|
||||
PageResult<AppCouponTemplateRespVO> result = convertAppPage(pageResult);
|
||||
if (MapUtil.isEmpty(couponTakeCountMap)) {
|
||||
return result;
|
||||
}
|
||||
for (AppCouponTemplateRespVO template : result.getList()) {
|
||||
// 每人领取数量无限制
|
||||
if (template.getTakeLimitCount() == -1) {
|
||||
template.setTakeStatus(false);
|
||||
continue;
|
||||
}
|
||||
// 检查已领取数量是否超过限领数量
|
||||
template.setTakeStatus(MapUtil.getInt(couponTakeCountMap, template.getId(), 0) >= template.getTakeLimitCount());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public class RewardActivityDO extends BaseDO {
|
|||
*/
|
||||
private List<Long> couponIds;
|
||||
/**
|
||||
* 赠送的优惠卷数量的数组
|
||||
* 赠送的优惠券数量的数组
|
||||
*/
|
||||
private List<Integer> couponCounts;
|
||||
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.github.yulichang.toolkit.MPJWrappers;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 优惠劵 Mapper
|
||||
|
@ -19,11 +27,11 @@ import java.util.List;
|
|||
@Mapper
|
||||
public interface CouponMapper extends BaseMapperX<CouponDO> {
|
||||
|
||||
default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO, Collection<Long> userIds) {
|
||||
default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>()
|
||||
.eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId())
|
||||
.eqIfPresent(CouponDO::getStatus, reqVO.getStatus())
|
||||
.inIfPresent(CouponDO::getUserId, userIds)
|
||||
.inIfPresent(CouponDO::getUserId, reqVO.getUserIds())
|
||||
.betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(CouponDO::getId));
|
||||
}
|
||||
|
@ -62,4 +70,37 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO @疯狂:这个是不是搞个 Map 就可以呀?
|
||||
default List<CouponTakeCountBO> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
|
||||
return BeanUtil.copyToList(selectMaps(MPJWrappers.lambdaJoin(CouponDO.class)
|
||||
.select(CouponDO::getTemplateId)
|
||||
.selectCount(CouponDO::getId, CouponTakeCountBO::getCount)
|
||||
.eq(CouponDO::getUserId, userId)
|
||||
.in(CouponDO::getTemplateId, templateIds)
|
||||
.groupBy(CouponDO::getTemplateId)), CouponTakeCountBO.class);
|
||||
}
|
||||
|
||||
default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
|
||||
Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
|
||||
Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
|
||||
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
|
||||
.collect(Collectors.joining(" OR "));
|
||||
return selectList(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getUserId, userId)
|
||||
.eq(CouponDO::getStatus, status)
|
||||
.le(CouponDO::getUsePrice, usePrice) // 价格小于等于,满足价格使用条件
|
||||
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一:全部
|
||||
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二:满足指定商品
|
||||
.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
|
||||
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三:满足指定分类
|
||||
.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
|
||||
}
|
||||
|
||||
default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {
|
||||
return selectList(new LambdaQueryWrapperX<CouponDO>()
|
||||
.eq(CouponDO::getStatus, status)
|
||||
.le(CouponDO::getValidEndTime, validEndTime)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
|
|||
.eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
|
||||
.betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
|
||||
.eqIfPresent(CouponTemplateDO::getProductScope, reqVO.getProductScope())
|
||||
.and(reqVO.getProductScopeValue() != null, w -> w.apply("FIND_IN_SET({0}, product_scope_values)",
|
||||
reqVO.getProductScopeValue()))
|
||||
.and(canTakeConsumer != null, canTakeConsumer)
|
||||
.orderByDesc(CouponTemplateDO::getId));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.promotion.job;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 优惠券过期 Job
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
public class CouponExpireJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private CouponService couponService;
|
||||
|
||||
@Override
|
||||
@TenantJob
|
||||
public String execute(String param) {
|
||||
int count = couponService.expireCoupon();
|
||||
return StrUtil.format("过期优惠券 {} 个", count);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,12 +3,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* 优惠劵 Service 接口
|
||||
*
|
||||
|
@ -18,11 +24,11 @@ public interface CouponService {
|
|||
|
||||
/**
|
||||
* 校验优惠劵,包括状态、有限期
|
||||
*
|
||||
* <p>
|
||||
* 1. 如果校验通过,则返回优惠劵信息
|
||||
* 2. 如果校验不通过,则直接抛出业务异常
|
||||
*
|
||||
* @param id 优惠劵编号
|
||||
* @param id 优惠劵编号
|
||||
* @param userId 用户编号
|
||||
* @return 优惠劵信息
|
||||
*/
|
||||
|
@ -31,9 +37,8 @@ public interface CouponService {
|
|||
/**
|
||||
* 校验优惠劵,包括状态、有限期
|
||||
*
|
||||
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
|
||||
*
|
||||
* @param coupon 优惠劵
|
||||
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
|
||||
*/
|
||||
void validCoupon(CouponDO coupon);
|
||||
|
||||
|
@ -124,4 +129,40 @@ public interface CouponService {
|
|||
takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计会员领取优惠券的数量
|
||||
*
|
||||
* @param templateIds 优惠券模板编号列表
|
||||
* @param userId 用户编号
|
||||
* @return 领取优惠券的数量
|
||||
*/
|
||||
default Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
|
||||
return convertMap(getTakeCountListByTemplateIds(templateIds, userId),
|
||||
CouponTakeCountBO::getTemplateId, CouponTakeCountBO::getCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计会员领取优惠券的数量
|
||||
*
|
||||
* @param templateIds 优惠券模板编号列表
|
||||
* @param userId 用户编号
|
||||
* @return 领取优惠券的数量
|
||||
*/
|
||||
List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户匹配的优惠券列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param matchReqVO 匹配参数
|
||||
* @return 优惠券列表
|
||||
*/
|
||||
List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
|
||||
|
||||
/**
|
||||
* 过期优惠券
|
||||
*
|
||||
* @return 过期数量
|
||||
*/
|
||||
int expireCoupon();
|
||||
}
|
||||
|
|
|
@ -2,15 +2,18 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
|
|||
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
|
||||
|
@ -18,12 +21,15 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
|
|||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.bo.CouponTakeCountBO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -39,6 +45,7 @@ import static java.util.Arrays.asList;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
public class CouponServiceImpl implements CouponService {
|
||||
|
@ -77,16 +84,16 @@ public class CouponServiceImpl implements CouponService {
|
|||
@Override
|
||||
public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {
|
||||
// 获得用户编号
|
||||
Set<Long> userIds = null;
|
||||
if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {
|
||||
userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
|
||||
Set<Long> userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
|
||||
MemberUserRespDTO::getId);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
pageReqVO.setUserIds(userIds);
|
||||
}
|
||||
// 分页查询
|
||||
return couponMapper.selectPage(pageReqVO, userIds);
|
||||
return couponMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,6 +182,56 @@ public class CouponServiceImpl implements CouponService {
|
|||
couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponTakeCountBO> getTakeCountListByTemplateIds(Collection<Long> templateIds, Long userId) {
|
||||
if (CollUtil.isEmpty(templateIds)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
|
||||
return couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId, CouponStatusEnum.UNUSED.getStatus(),
|
||||
matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expireCoupon() {
|
||||
// 1. 查询待过期的优惠券
|
||||
List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe(
|
||||
CouponStatusEnum.UNUSED.getStatus(), LocalDateTime.now());
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2. 遍历执行
|
||||
int count = 0;
|
||||
for (CouponDO coupon : list) {
|
||||
try {
|
||||
boolean success = getSelf().expireCoupon(coupon);
|
||||
if (success) {
|
||||
count++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId(), e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean expireCoupon(CouponDO coupon) {
|
||||
// 更新记录状态
|
||||
int updateRows = couponMapper.updateByIdAndStatus(coupon.getId(), CouponStatusEnum.UNUSED.getStatus(),
|
||||
new CouponDO().setStatus(CouponStatusEnum.EXPIRE.getStatus()));
|
||||
if (updateRows == 0) {
|
||||
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", coupon.getId());
|
||||
return false;
|
||||
}
|
||||
log.info("[expireCoupon][coupon({}) 更新为已过期成功]", coupon.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验优惠券是否可以领取
|
||||
*
|
||||
|
@ -211,7 +268,7 @@ public class CouponServiceImpl implements CouponService {
|
|||
/**
|
||||
* 过滤掉达到领取上线的用户
|
||||
*
|
||||
* @param userIds 用户编号数组
|
||||
* @param userIds 用户编号数组
|
||||
* @param couponTemplate 优惠劵模版
|
||||
*/
|
||||
private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
|
||||
|
@ -228,4 +285,12 @@ public class CouponServiceImpl implements CouponService {
|
|||
userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
private CouponServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.promotion.service.coupon.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 优惠券领取数量 BO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
public class CouponTakeCountBO {
|
||||
|
||||
/**
|
||||
* 优惠劵模板编号
|
||||
*/
|
||||
private Long templateId;
|
||||
/**
|
||||
* 领取数量
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-statistics-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
statistics 模块 API,暴露给其它模块调用
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 参数校验 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* TODO 占位,无特殊含义
|
||||
*/
|
||||
package cn.iocoder.yudao.module.statistics.api;
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* TODO 占位,无特殊含义
|
||||
*/
|
||||
package cn.iocoder.yudao.module.statistics.enums;
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-statistics-biz</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
statistics 模块,主要实现统计相关功能
|
||||
例如:统计商品、会员、交易等功能。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-statistics-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-promotion-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-trade-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-member-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* TODO
|
||||
* 1. 会员总数据
|
||||
* 2. 性别统计
|
||||
* 3. 渠道统计
|
||||
* 4. 地域统计
|
||||
* 5. 会员概览
|
||||
*/
|
||||
package cn.iocoder.yudao.module.statistics.controller.member;
|
|
@ -0,0 +1 @@
|
|||
package cn.iocoder.yudao.module.statistics.controller;
|
|
@ -0,0 +1,41 @@
|
|||
package cn.iocoder.yudao.module.statistics.controller.product;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductSpuStatisticsDO;
|
||||
import cn.iocoder.yudao.module.statistics.dal.mysql.product.ProductStatisticsDO;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "管理后台 - 商品统计")
|
||||
@RestController
|
||||
@RequestMapping("/statistics/product")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class ProductStatisticsController {
|
||||
|
||||
// TODO @麦子:返回 ProductStatisticsComparisonResp, 里面有两个字段,一个是选择的时间范围的合计结果,一个是对比的时间范围的合计结果;
|
||||
// 例如说,选择时间范围是 2023-10-01 ~ 2023-10-02,那么对比就是 2023-09-30,再倒推 2 天;
|
||||
public CommonResult<Object> getProductStatisticsComparison() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO @麦子:查询指定时间范围内的商品统计数据;DO 到时需要改成 VO 哈
|
||||
public CommonResult<List<ProductStatisticsDO>> getProductStatisticsList(
|
||||
LocalDateTime[] times) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO @麦子:查询指定时间范围内的商品 SPU 统计数据;DO 到时需要改成 VO 哈
|
||||
// 入参是分页参数 + 时间范围 + 排序字段
|
||||
public CommonResult<PageResult<ProductSpuStatisticsDO>> getProductSpuStatisticsPage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue