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

 Conflicts:
	sql/optional/mall/mall.sql
This commit is contained in:
YunaiV 2022-09-04 21:06:28 +08:00
commit 833fd33844
618 changed files with 49151 additions and 1382 deletions

View File

@ -23,8 +23,8 @@
>
> 😜 给项目点点 Star 吧,这对我们真的很重要!
* 前端 Vue2 版本采用 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
* 前端 Vue3 版本采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin)
* 管理后台的 Vue3 版本采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) Vue2 版本采用 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
* 管理后台的移动端采用 [uni-app](https://github.com/dcloudio/uni-app) 方案,一份代码多终端适配,同时支持 APP、小程序、H5
* 后端采用 Spring Boot、MySQL + MyBatis Plus、Redis + Redisson
* 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等
* 权限认证使用 Spring Security & Token & Redis支持多终端、多种用户的认证系统支持 SSO 单点登录
@ -156,8 +156,9 @@ ps核心功能已经实现正在对接微信小程序中...
| `yudao-dependencies` | Maven 依赖版本管理 |
| `yudao-framework` | Java 框架拓展 |
| `yudao-server` | 管理后台 + 用户 APP 的服务端 |
| `yudao-ui-admin` | Vue2 管理后台的 UI 界面 |
| `yudao-ui-admin-vue3` | Vue3 管理后台的 UI 界面 |
| `yudao-ui-admin` | 管理后台的 Vue2 前端项目 |
| `yudao-ui-admin-vue3` | 管理后台的 Vue3 前端项目 |
| `yudao-ui-admin-uniapp` | 管理后台的 uni-app 多端项目 |
| `yudao-ui-app` | 用户 APP 的 UI 界面 |
| `yudao-module-system` | 系统功能的 Module 模块 |
| `yudao-module-member` | 会员中心的 Module 模块 |
@ -169,48 +170,55 @@ ps核心功能已经实现正在对接微信小程序中...
### 后端
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|-----------------------|-----------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.6.10 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.11 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.17.4 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.20 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.6.5 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.3 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.7.0 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.3 | [文档](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.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.6.7 | [文档](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.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](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.0.0 | - |
|---------------------------------------------------------------------------------------------|-----------------------|---------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.6.10 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.11 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.17.4 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.20 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.6.5 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.3 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.7.2 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Knife4j](https://gitee.com/xiaoym/knife4j) | Swagger 增强 UI 实现 | 3.0.3 | [文档](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.5.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.6.7 | [文档](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.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](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.0.0 | - |
### Vue2 前端
### [管理后台 Vue2 前端](./yudao-ui-admin)
| 框架 | 说明 | 版本 |
|------------------------------------------------------------------------------|---------------|--------|
| [Vue](https://cn.vuejs.org/index.html) | JavaScript 框架 | 2.6.12 |
| [Vue Element Admin](https://panjiachen.github.io/vue-element-admin-site/zh/) | 后台前端解决方案 | - |
### Vue3 前端
### [管理后台 Vue3 前端](./yudao-ui-admin-vue3)
| 框架 | 说明 | 版本 |
|----------------------------------------------------------------------|-----------------|--------|
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.37 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.4 |
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.12 |
| [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.7.4 |
| [pinia](https://pinia.vuejs.org/) | vuex5 | 2.0.17 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 |
| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |
| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.1 |
### [管理后台 uni-app 跨端](./yudao-ui-admin-uniapp)
| 框架 | 说明 | 版本 |
|----------------------------------------------------------------------|------------------|--------|
| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.37 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.3 |
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.11 |
| [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.7.4 |
| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.17 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.1.10 |
| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |
| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.1 |
| [uni-app](hhttps://github.com/dcloudio/uni-app) | 跨平台框架 | 2.0.0 |
| [uni-ui](https://github.com/dcloudio/uni-ui) | 基于 uni-app 的 UI 框架 | 1.4.20 |
## 🐷 演示图
@ -262,3 +270,13 @@ ps核心功能已经实现正在对接微信小程序中...
| 模块 | biu | biu | biu |
|---------|------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------|
| 报表设计器 | ![数据报表](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-数据报表.jpg?imageView2/2/format/webp/w/1280) | ![图形报表](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-图形报表.jpg?imageView2/2/format/webp/w/1280) | ![报表设计器-打印设计](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-打印设计.jpg?imageView2/2/format/webp/w/1280) |
### 移动端(管理后台)
| biu | biu | biu |
|------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------|
| ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/01.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/02.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/03.png?imageView2/2/format/webp) |
| ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/04.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/05.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/06.png?imageView2/2/format/webp) |
| ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/07.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/08.png?imageView2/2/format/webp) | ![](https://static.iocoder.cn/images/ruoyi-vue-pro/admin-uniapp/09.png?imageView2/2/format/webp) |
目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。

View File

@ -27,7 +27,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.6.3-snapshot</revision>
<revision>1.6.4-snapshot</revision>
<!-- Maven 相关 -->
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>

View File

@ -2657,8 +2657,3 @@ INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`,
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
-- 积木报表菜单
INSERT INTO `system_menu` VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'chart', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` VALUES (1282, '积木报表', '', 2, 1, 1281, 'jm-report', '#', 'visualization/jm/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-10 20:33:26', b'0');

7
sql/mysql/update.sql Normal file
View File

@ -0,0 +1,7 @@
-- ----------------------------
-- 升级SQL文件全新安装只需要执行ruoyi-vue-pro.sql文件即可
-- 1.6.2 ==> 1.6.3
-- ----------------------------
-- 积木报表菜单
INSERT INTO `system_menu` VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'chart', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` VALUES (1282, '积木报表', '', 2, 1, 1281, 'jm-report', '#', 'visualization/jm/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-10 20:33:26', b'0');

View File

@ -257,5 +257,7 @@ INSERT INTO `system_menu` VALUES (1264, '客户端查询', 'system:oauth2-client
INSERT INTO `system_menu` VALUES (1265, '客户端创建', 'system:oauth2-client:create', 3, 2, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:23', b'0');
INSERT INTO `system_menu` VALUES (1266, '客户端更新', 'system:oauth2-client:update', 3, 3, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:28', b'0');
INSERT INTO `system_menu` VALUES (1267, '客户端删除', 'system:oauth2-client:delete', 3, 4, 1263, '', '', '', 0, b'1', b'1', '', '2022-05-10 16:26:33', '1', '2022-05-11 00:31:33', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1281, '可视化报表', '', 1, 12, 0, '/visualization', 'ep:histogram', NULL, 0, b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2022-07-10 20:33:30', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1282, '积木报表', '', 2, 1, 1281, 'jimu-report', 'ep:histogram', 'visualization/jmreport/index', 0, b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2022-07-28 21:17:34', b'0');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -22,23 +22,23 @@ SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
DROP TABLE IF EXISTS `market_activity`;
CREATE TABLE `market_activity` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '活动标题',
`activity_type` tinyint NOT NULL COMMENT '活动类型',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
`delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
`time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_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
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '活动标题',
`activity_type` tinyint NOT NULL COMMENT '活动类型',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`invalid_time` datetime NULL DEFAULT NULL COMMENT '失效时间',
`delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
`time_limited_discount` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`full_privilege` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '限制折扣字符串使用 JSON 序列化成字符串存储',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_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 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '促销活动';
-- ----------------------------
@ -52,20 +52,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `market_banner`;
CREATE TABLE `market_banner` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片URL',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跳转地址',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_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 '租户编号',
`sort` tinyint NULL DEFAULT NULL COMMENT '排序',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`) USING BTREE
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Banner编号',
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'Banner标题',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片URL',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '活动状态',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '跳转地址',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_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 '租户编号',
`sort` tinyint NULL DEFAULT NULL COMMENT '排序',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'Banner管理';
-- ----------------------------
@ -79,22 +79,22 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `member_address`;
CREATE TABLE `member_address` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
`user_id` bigint NOT NULL COMMENT '用户编号',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
`area_id` bigint NOT NULL COMMENT '地区编码',
`post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮编',
`detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
`defaulted` bit(1) NOT 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,
INDEX `idx_userId`(`user_id` ASC) USING BTREE
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
`user_id` bigint NOT NULL COMMENT '用户编号',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
`area_id` bigint NOT NULL COMMENT '地区编码',
`post_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮编',
`detail_address` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
`defaulted` bit(1) NOT 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,
INDEX `idx_userId`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址';
-- ----------------------------
@ -109,19 +109,19 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_brand`;
CREATE TABLE `product_brand` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌名称',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌图片',
`sort` int NULL DEFAULT 0 COMMENT '品牌排序',
`description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '品牌描述',
`status` tinyint NOT 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
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌名称',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '品牌图片',
`sort` int NULL DEFAULT 0 COMMENT '品牌排序',
`description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '品牌描述',
`status` tinyint NOT 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 = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品品牌';
-- ----------------------------
@ -136,20 +136,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_category`;
CREATE TABLE `product_category` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
`parent_id` bigint NOT NULL COMMENT '父分类编号',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类图片',
`sort` int NULL DEFAULT 0 COMMENT '分类排序',
`description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '分类描述',
`status` tinyint NOT 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
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
`parent_id` bigint NOT NULL COMMENT '父分类编号',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
`pic_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类图片',
`sort` int NULL DEFAULT 0 COMMENT '分类排序',
`description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '分类描述',
`status` tinyint NOT 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 = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品分类';
-- ----------------------------
@ -164,17 +164,17 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_property`;
CREATE TABLE `product_property` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格名称',
`status` tinyint NULL DEFAULT NULL COMMENT '状态 0 开启 1 禁用',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_name`(`name`(32) ASC) USING BTREE COMMENT '规格名称索引'
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格名称',
`status` tinyint NULL DEFAULT NULL COMMENT '状态 0 开启 1 禁用',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_name`(`name`(32) ASC) USING BTREE COMMENT '规格名称索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格名称';
-- ----------------------------
@ -188,17 +188,17 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_property_value`;
CREATE TABLE `product_property_value` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`property_id` bigint NULL DEFAULT NULL COMMENT '规格键id',
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格值名字',
`status` tinyint NULL DEFAULT NULL COMMENT '状态 1 开启 2 禁用',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`property_id` bigint NULL DEFAULT NULL COMMENT '规格键id',
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格值名字',
`status` tinyint NULL DEFAULT NULL COMMENT '状态 1 开启 2 禁用',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格值';
-- ----------------------------
@ -212,25 +212,25 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_sku`;
CREATE TABLE `product_sku` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`spu_id` bigint NOT NULL COMMENT 'spu编号',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
`name` varchar(128) DEFAULT NULL COMMENT '商品 SKU 名字',
`properties` varchar(128) DEFAULT NULL COMMENT '规格值数组-json格式 [{propertId: , valueId: }, {propertId: , valueId: }]',
`price` int NOT NULL DEFAULT '-1' COMMENT '销售价格单位',
`market_price` int DEFAULT NULL COMMENT '市场价',
`cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价单位 ',
`pic_url` varchar(128) NOT NULL COMMENT '图片地址',
`stock` int DEFAULT NULL COMMENT '库存',
`warn_stock` int DEFAULT NULL COMMENT '预警库存',
`volume` double DEFAULT NULL COMMENT '商品体积',
`weight` double DEFAULT NULL COMMENT '商品重量',
`bar_code` varchar(64) DEFAULT NULL COMMENT '条形码',
`status` tinyint DEFAULT NULL COMMENT '状态 0-正常 1-禁用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
`updater` double(64,0) DEFAULT NULL COMMENT '更新人',
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`spu_id` bigint NOT NULL COMMENT 'spu编号',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
`name` varchar(128) DEFAULT NULL COMMENT '商品 SKU 名字',
`properties` varchar(128) DEFAULT NULL COMMENT '规格值数组-json格式 [{propertId: , valueId: }, {propertId: , valueId: }]',
`price` int NOT NULL DEFAULT '-1' COMMENT '销售价格单位',
`market_price` int DEFAULT NULL COMMENT '市场价',
`cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价单位 ',
`pic_url` varchar(128) NOT NULL COMMENT '图片地址',
`stock` int DEFAULT NULL COMMENT '库存',
`warn_stock` int DEFAULT NULL COMMENT '预警库存',
`volume` double DEFAULT NULL COMMENT '商品体积',
`weight` double DEFAULT NULL COMMENT '商品重量',
`bar_code` varchar(64) DEFAULT NULL COMMENT '条形码',
`status` tinyint DEFAULT NULL COMMENT '状态 0-正常 1-禁用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
`updater` double(64,0) DEFAULT NULL COMMENT '更新人',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='商品sku';
@ -246,33 +246,33 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `product_spu`;
CREATE TABLE `product_spu` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
`brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
`category_id` bigint NOT NULL COMMENT '分类id',
`spec_type` int NOT NULL COMMENT '规格类型0 单规格 1 多规格',
`code` varchar(128) DEFAULT NULL COMMENT '商品编码',
`name` varchar(128) NOT NULL COMMENT '商品名称',
`sell_point` varchar(128) DEFAULT NULL COMMENT '卖点',
`description` text COMMENT '描述',
`pic_urls` varchar(1024) DEFAULT '' COMMENT '商品轮播图地址\n 数组以逗号分隔\n 最多上传15张',
`video_url` varchar(128) DEFAULT NULL COMMENT '商品视频',
`market_price` int DEFAULT NULL COMMENT '市场价单位使用',
`min_price` int DEFAULT NULL COMMENT '最小价格单位使用',
`max_price` int DEFAULT NULL COMMENT '最大价格单位使用',
`total_stock` int NOT NULL DEFAULT '0' COMMENT '总库存',
`show_stock` int DEFAULT '0' COMMENT '是否展示库存',
`sales_count` int DEFAULT '0' COMMENT '商品销量',
`virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
`click_count` int DEFAULT '0' COMMENT '商品点击量',
`status` bit(1) DEFAULT NULL COMMENT '上下架状态 0 上架开启 1 下架禁用-1 回收',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) DEFAULT NULL COMMENT '更新人',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
`brand_id` int DEFAULT NULL COMMENT '商品品牌编号',
`category_id` bigint NOT NULL COMMENT '分类id',
`spec_type` int NOT NULL COMMENT '规格类型0 单规格 1 多规格',
`code` varchar(128) DEFAULT NULL COMMENT '商品编码',
`name` varchar(128) NOT NULL COMMENT '商品名称',
`sell_point` varchar(128) DEFAULT NULL COMMENT '卖点',
`description` text COMMENT '描述',
`pic_urls` varchar(1024) DEFAULT '' COMMENT '商品轮播图地址\n 数组以逗号分隔\n 最多上传15张',
`video_url` varchar(128) DEFAULT NULL COMMENT '商品视频',
`market_price` int DEFAULT NULL COMMENT '市场价单位使用',
`min_price` int DEFAULT NULL COMMENT '最小价格单位使用',
`max_price` int DEFAULT NULL COMMENT '最大价格单位使用',
`total_stock` int NOT NULL DEFAULT '0' COMMENT '总库存',
`show_stock` int DEFAULT '0' COMMENT '是否展示库存',
`sales_count` int DEFAULT '0' COMMENT '商品销量',
`virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量',
`click_count` int DEFAULT '0' COMMENT '商品点击量',
`status` bit(1) DEFAULT NULL COMMENT '上下架状态 0 上架开启 1 下架禁用-1 回收',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序字段',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`creator` varchar(64) DEFAULT NULL COMMENT '创建人',
`updater` varchar(64) DEFAULT NULL COMMENT '更新人',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='商品spu';
-- ----------------------------

View File

@ -14,7 +14,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.6.3-snapshot</revision>
<revision>1.6.4-snapshot</revision>
<!-- 统一依赖管理 -->
<spring.boot.version>2.6.10</spring.boot.version>
<!-- Web 相关 -->
@ -41,14 +41,14 @@
<jedis-mock.version>0.1.16</jedis-mock.version>
<mockito-inline.version>4.0.0</mockito-inline.version>
<!-- Bpm 工作流相关 -->
<flowable.version>6.7.0</flowable.version>
<flowable.version>6.7.2</flowable.version>
<!-- 工具类相关 -->
<jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
<lombok.version>1.18.20</lombok.version>
<mapstruct.version>1.4.1.Final</mapstruct.version>
<hutool.version>5.7.22</hutool.version>
<hutool.version>5.8.5</hutool.version>
<easyexcel.verion>3.1.1</easyexcel.verion>
<velocity.version>2.2</velocity.version>
<velocity.version>2.3</velocity.version>
<screw.version>1.0.5</screw.version>
<fastjson.version>1.2.83</fastjson.version>
<guava.version>30.1.1-jre</guava.version>
@ -57,14 +57,16 @@
<commons-net.version>3.8.0</commons-net.version>
<jsch.version>0.1.55</jsch.version>
<tika-core.version>2.4.1</tika-core.version>
<aj-captcha.version>1.3.0</aj-captcha.version>
<!-- 三方云服务相关 -->
<minio.version>8.2.2</minio.version>
<aliyun-java-sdk-core.version>4.5.25</aliyun-java-sdk-core.version>
<aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version>
<tencentcloud-sdk-java.version>3.1.471</tencentcloud-sdk-java.version>
<aliyun-java-sdk-core.version>4.6.0</aliyun-java-sdk-core.version>
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
<tencentcloud-sdk-java.version>3.1.561</tencentcloud-sdk-java.version>
<yunpian-java-sdk.version>1.2.7</yunpian-java-sdk.version>
<justauth.version>1.4.0</justauth.version>
<jimureport.version>1.5.2</jimureport.version>
<xercesImpl.version>2.12.0</xercesImpl.version>
</properties>
<dependencyManagement>
@ -129,6 +131,11 @@
<artifactId>yudao-spring-boot-starter-biz-error-code</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-captcha</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring 核心 -->
<dependency>
@ -451,6 +458,12 @@
<version>${tika-core.version}</version>
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>${aj-captcha.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
@ -565,6 +578,11 @@
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>${jimureport.version}</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>${xercesImpl.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -39,6 +39,7 @@
<module>yudao-spring-boot-starter-biz-error-code</module>
<module>yudao-spring-boot-starter-flowable</module>
<module>yudao-spring-boot-starter-captcha</module>
</modules>
<artifactId>yudao-framework</artifactId>

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.framework.common.util.collection;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.ArrayUtil;
import java.util.Collection;
@ -44,7 +45,7 @@ public class ArrayUtils {
if (CollectionUtil.isEmpty(from)) {
return (T[]) (new Object[0]);
}
return ArrayUtil.toArray(from, (Class<T>) CollectionUtil.getElementType(from.iterator()));
return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator()));
}
public static <T> T get(T[] array, int index) {

View File

@ -17,16 +17,15 @@ import java.util.regex.Pattern;
*/
public class ValidationUtils {
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[189]))\\d{8}$");
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
public static boolean isMobile(String mobile) {
if (StrUtil.length(mobile) != 11) {
return false;
}
// TODO 芋艿后面完善手机校验
return true;
return StringUtils.hasText(mobile)
&& PATTERN_MOBILE.matcher(mobile).matches();
}
public static boolean isURL(String url) {

View File

@ -27,7 +27,7 @@ public class DictFrameworkUtils {
/**
* 针对 {@link #getDictDataLabel(String, String)} 的缓存
*/
private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> getDictDataCache = CacheUtils.buildAsyncReloadingCache(
private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> GET_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache(
Duration.ofMinutes(1L), // 过期时间 1 分钟
new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
@ -41,7 +41,7 @@ public class DictFrameworkUtils {
/**
* 针对 {@link #parseDictDataValue(String, String)} 的缓存
*/
private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> parseDictDataCache = CacheUtils.buildAsyncReloadingCache(
private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> PARSE_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache(
Duration.ofMinutes(1L), // 过期时间 1 分钟
new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
@ -59,12 +59,12 @@ public class DictFrameworkUtils {
@SneakyThrows
public static String getDictDataLabel(String dictType, String value) {
return getDictDataCache.get(new KeyValue<>(dictType, value)).getLabel();
return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel();
}
@SneakyThrows
public static String parseDictDataValue(String dictType, String label) {
return parseDictDataCache.get(new KeyValue<>(dictType, label)).getValue();
return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue();
}
}

View File

@ -52,12 +52,18 @@
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.17.9.ALL</version>
<version>4.31.72.ALL</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.1.9.B</version>
<version>4.3.8.B</version>
</dependency>
<!-- TODO 芋艿:清理 -->

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.framework.pay.core.client.impl;
import cn.hutool.extra.validation.ValidationUtil;
import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
@ -10,6 +9,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO;
import lombok.extern.slf4j.Slf4j;
import javax.validation.Validation;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
/**
@ -79,7 +80,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
@Override
public final PayCommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
ValidationUtil.validate(reqDTO);
Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
// 执行短信发送
PayCommonResult<?> result;
try {

View File

@ -53,9 +53,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
"lrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZ" +
"ikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
// TODO @tina= 前后要有空格哈
@InjectMocks
AlipayQrPayClient client=new AlipayQrPayClient(10L,config);
AlipayQrPayClient client = new AlipayQrPayClient(10L,config);
@Mock
private DefaultAlipayClient defaultAlipayClient;

View File

@ -35,12 +35,12 @@
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>4.3.4.B</version>
<version>4.3.8.B</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
<version>4.3.4.B</version>
<version>4.3.8.B</version>
</dependency>
<!-- TODO 芋艿:清理 -->
</dependencies>

View File

@ -0,0 +1,39 @@
<?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-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-spring-boot-starter-captcha</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>验证码拓展
1. 基于 aj-captcha 实现滑块验证码文档https://ajcaptcha.beliefteam.cn/captcha-doc/
</description>
<dependencies>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-redis</artifactId>
</dependency>
<!-- 验证码相关 -->
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.framework.captcha.config;
import cn.hutool.core.util.ClassUtil;
import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
import com.anji.captcha.service.CaptchaCacheService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
public class YudaoCaptchaConfiguration {
static {
// 手动加载 Lock4jRedisKeyConstants 因为它不会被使用到
// 如果不加载会导致 Redis 监控看到它的 Redis Key 枚举
ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName());
}
@Bean
public CaptchaCacheService captchaCacheService(StringRedisTemplate stringRedisTemplate) {
return new RedisCaptchaServiceImpl(stringRedisTemplate);
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.framework.captcha.core.enums;
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
import com.anji.captcha.model.vo.PointVO;
import org.redisson.api.RLock;
import java.time.Duration;
import java.util.Map;
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH;
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
/**
* 验证码 Redis Key 枚举类
*
* @author 芋道源码
*/
public interface CaptchaRedisKeyConstants {
RedisKeyDefine AJ_CAPTCHA_REQ_LIMIT = new RedisKeyDefine("验证码的请求限流",
"AJ.CAPTCHA.REQ.LIMIT-%s-%s",
STRING, Integer.class, Duration.ofSeconds(60)); // 例如说验证失败 5 get 接口锁定
RedisKeyDefine AJ_CAPTCHA_RUNNING = new RedisKeyDefine("验证码的坐标",
"RUNNING:CAPTCHA:%s", // AbstractCaptchaService.REDIS_CAPTCHA_KEY
STRING, PointVO.class, Duration.ofSeconds(120)); // {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5}
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.framework.captcha.core.service;
import com.anji.captcha.service.CaptchaCacheService;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 基于 Redis 实现验证码的存储
*
* @author 星语
*/
@NoArgsConstructor // 保证 aj-captcha SPI 创建
@AllArgsConstructor
public class RedisCaptchaServiceImpl implements CaptchaCacheService {
@Resource // 保证 aj-captcha SPI 创建时的注入
private StringRedisTemplate stringRedisTemplate;
@Override
public String type() {
return "redis";
}
@Override
public void set(String key, String value, long expiresInSeconds) {
stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
}
@Override
public boolean exists(String key) {
return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
}
@Override
public void delete(String key) {
stringRedisTemplate.delete(key);
}
@Override
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
@Override
public Long increment(String key, long val) {
return stringRedisTemplate.opsForValue().increment(key,val);
}
}

View File

@ -0,0 +1,7 @@
/**
* 验证码拓展
* 1. 基于 aj-captcha 实现滑块验证码文档https://ajcaptcha.beliefteam.cn/captcha-doc/
*
* @author 星语
*/
package cn.iocoder.yudao.framework.captcha;

View File

@ -0,0 +1 @@
cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl

View File

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.iocoder.yudao.framework.captcha.config.YudaoCaptchaConfiguration

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -16,11 +16,11 @@ import java.util.Set;
*/
public class JsonLongSetTypeHandler extends AbstractJsonTypeHandler<Object> {
private static final TypeReference<Set<Long>> typeReference = new TypeReference<Set<Long>>(){};
private static final TypeReference<Set<Long>> TYPE_REFERENCE = new TypeReference<Set<Long>>(){};
@Override
protected Object parse(String json) {
return JsonUtils.parseObject(json, typeReference);
return JsonUtils.parseObject(json, TYPE_REFERENCE);
}
@Override

View File

@ -11,18 +11,18 @@ public class RedisKeyRegistry {
/**
* Redis RedisKeyDefine 数组
*/
private static final List<RedisKeyDefine> defines = new ArrayList<>();
private static final List<RedisKeyDefine> DEFINES = new ArrayList<>();
public static void add(RedisKeyDefine define) {
defines.add(define);
DEFINES.add(define);
}
public static List<RedisKeyDefine> list() {
return defines;
return DEFINES;
}
public static int size() {
return defines.size();
return DEFINES.size();
}
}

View File

@ -81,7 +81,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
/**
* 配置 URL 的安全配置
*
* <p>
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
@ -109,8 +109,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
.headers().frameOptions().disable().and()
// 一堆自定义的 Spring Security 处理器
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
// 登录登录暂时不使用 Spring Security 的拓展点主要考虑一方面拓展多用户多种登录方式相对复杂一方面用户的学习成本较高
.accessDeniedHandler(accessDeniedHandler);
// 登录登录暂时不使用 Spring Security 的拓展点主要考虑一方面拓展多用户多种登录方式相对复杂一方面用户的学习成本较高
// 获得 @PermitAll 带来的 URL 列表免登录
Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations();
@ -118,23 +118,25 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
httpSecurity
// 全局共享规则
.authorizeRequests()
// 1.1 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证
.antMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
// 1.4 设置 App API 无需认证
.antMatchers(buildAppApi("/**")).permitAll()
// 1.1 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
.antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证
.antMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
// 1.4 设置 App API 无需认证
.antMatchers(buildAppApi("/**")).permitAll()
// 1.5 验证码captcha 允许匿名访问
.antMatchers("/captcha/get", "/captcha/check").permitAll()
// 每个项目的自定义规则
.and().authorizeRequests(registry -> // 下面循环设置自定义规则
authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))
// 兜底规则必须认证
.authorizeRequests()
.anyRequest().authenticated()
.anyRequest().authenticated()
;
// 添加 Token Filter

View File

@ -17,19 +17,19 @@ public class TransmittableThreadLocalSecurityContextHolderStrategy implements Se
/**
* 使用 TransmittableThreadLocal 作为上下文
*/
private static final ThreadLocal<SecurityContext> contextHolder = new TransmittableThreadLocal<>();
private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>();
@Override
public void clearContext() {
contextHolder.remove();
CONTEXT_HOLDER.remove();
}
@Override
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
SecurityContext ctx = CONTEXT_HOLDER.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
CONTEXT_HOLDER.set(ctx);
}
return ctx;
}
@ -37,7 +37,7 @@ public class TransmittableThreadLocalSecurityContextHolderStrategy implements Se
@Override
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
CONTEXT_HOLDER.set(context);
}
@Override

View File

@ -57,7 +57,7 @@ public class BpmMessageServiceImpl implements BpmMessageService {
templateParams.put("taskName", reqDTO.getTaskName());
templateParams.put("startUserNickname", reqDTO.getStartUserNickname());
templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()));
smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(),
smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(),
BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams));
}

View File

@ -154,12 +154,12 @@ public class CodegenServiceImpl implements CodegenService {
// 构建 CodegenColumnDO 数组只同步新增的字段
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
Set<String> codegenColumnNames = CollectionUtils.convertSet(codegenColumns, CodegenColumnDO::getColumnName);
// 移除已经存在的字段
tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()));
// 计算需要删除的字段
Set<String> tableFieldNames = CollectionUtils.convertSet(tableFields, TableField::getName);
Set<Long> deleteColumnIds = codegenColumns.stream().filter(column -> !tableFieldNames.contains(column.getColumnName()))
.map(CodegenColumnDO::getId).collect(Collectors.toSet());
// 移除已经存在的字段
tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()));
if (CollUtil.isEmpty(tableFields) && CollUtil.isEmpty(deleteColumnIds)) {
throw exception(CODEGEN_SYNC_NONE_CHANGE);
}

View File

@ -31,7 +31,7 @@ public class CodegenBuilder {
* 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, CodegenColumnListConditionEnum> columnListOperationConditionMappings =
private static final Map<String, CodegenColumnListConditionEnum> COLUMN_LIST_OPERATION_CONDITION_MAPPINGS =
MapUtil.<String, CodegenColumnListConditionEnum>builder()
.put("name", CodegenColumnListConditionEnum.LIKE)
.put("time", CodegenColumnListConditionEnum.BETWEEN)
@ -42,7 +42,7 @@ public class CodegenBuilder {
* 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射
* 注意字段的匹配以后缀的方式
*/
private static final Map<String, CodegenColumnHtmlTypeEnum> columnHtmlTypeMappings =
private static final Map<String, CodegenColumnHtmlTypeEnum> COLUMN_HTML_TYPE_MAPPINGS =
MapUtil.<String, CodegenColumnHtmlTypeEnum>builder()
.put("status", CodegenColumnHtmlTypeEnum.RADIO)
.put("sex", CodegenColumnHtmlTypeEnum.RADIO)
@ -143,7 +143,7 @@ public class CodegenBuilder {
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 对于主键列表过滤不需要传递
// 处理 listOperationCondition 字段
columnListOperationConditionMappings.entrySet().stream()
COLUMN_LIST_OPERATION_CONDITION_MAPPINGS.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));
if (column.getListOperationCondition() == null) {
@ -155,7 +155,7 @@ public class CodegenBuilder {
private void processColumnUI(CodegenColumnDO column) {
// 基于后缀进行匹配
columnHtmlTypeMappings.entrySet().stream()
COLUMN_HTML_TYPE_MAPPINGS.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时设置为 radio 类型.

View File

@ -96,7 +96,7 @@
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)

View File

@ -5,31 +5,31 @@ const request = useAxios()
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
// 查询${table.classComment}列表
export const getPostPageApi = async (params: ${simpleClassName}PageReqVO) => {
export const get${simpleClassName}PageApi = async (params: ${simpleClassName}PageReqVO) => {
return await request.get({ url: '${baseURL}/page', params })
}
// 查询${table.classComment}详情
export const getPostApi = async (id: number) => {
export const get${simpleClassName}Api = async (id: number) => {
return await request.get({ url: '${baseURL}/get?id=' + id })
}
// 新增${table.classComment}
export const createPostApi = async (data: ${simpleClassName}VO) => {
export const create${simpleClassName}Api = async (data: ${simpleClassName}VO) => {
return await request.post({ url: '${baseURL}/create', data })
}
// 修改${table.classComment}
export const updatePostApi = async (data: ${simpleClassName}VO) => {
export const update${simpleClassName}Api = async (data: ${simpleClassName}VO) => {
return await request.put({ url: '${baseURL}/update', data })
}
// 删除${table.classComment}
export const deletePostApi = async (id: number) => {
export const delete${simpleClassName}Api = async (id: number) => {
return await request.delete({ url: '${baseURL}/delete?id=' + id })
}
// 导出${table.classComment} Excel
export const exportPostApi = async (params: ${simpleClassName}ExcelReqVO) => {
export const export${simpleClassName}Api = async (params: ${simpleClassName}ExcelReqVO) => {
return await request.download({ url: '${baseURL}/export-excel', params })
}

View File

@ -6,9 +6,9 @@
import { useTable } from '@/hooks/web/useTable'
import { useI18n } from '@/hooks/web/useI18n'
import { FormExpose } from '@/components/Form'
import type { ${simpleClassName}VO } from '@/api/system/post/types'
import { rules, allSchemas } from './post.data'
import * as ${simpleClassName}Api from '@/api/system/post'
import type { ${simpleClassName}VO } from '@/api/${table.moduleName}/${simpleClassName}/types'
import { rules, allSchemas } from './${simpleClassName}.data'
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${simpleClassName}'
const { t } = useI18n() // 国际化
// ========== 列表相关 ==========

View File

@ -66,7 +66,7 @@ public class ProductSpuController {
@GetMapping("/list")
@ApiOperation("获得商品spu列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = Long.class)
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<List<SpuRespVO>> getSpuList(@RequestParam("ids") Collection<Long> ids) {
List<ProductSpuDO> list = spuService.getSpuList(ids);

View File

@ -12,8 +12,7 @@ public interface ErrorCodeConstants {
// ========== AUTH 模块 1002000000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确,原因:{}");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1002000007, "手机号不存在");

View File

@ -97,6 +97,11 @@
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-captcha</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -55,7 +55,6 @@ public class AuthController {
private PermissionService permissionService;
@Resource
private SocialUserService socialUserService;
@Resource
private SecurityProperties securityProperties;

View File

@ -35,13 +35,11 @@ public class AuthLoginReqVO {
// ========== 图片验证码相关 ==========
@ApiModelProperty(value = "验证码", required = true, example = "1024", notes = "验证码开启时,需要传递")
@ApiModelProperty(value = "验证码", required = true,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==",
notes = "验证码开启时,需要传递")
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
private String code;
@ApiModelProperty(value = "验证码的唯一标识", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62", notes = "验证码开启时,需要传递")
@NotEmpty(message = "唯一标识不能为空", groups = CodeEnableGroup.class)
private String uuid;
private String captchaVerification;
// ========== 绑定社交登录时需要传递如下参数 ==========

View File

@ -1,3 +0,0 @@
### 请求 /captcha/get-image 接口 => 成功
GET {{baseUrl}}/system/captcha/get-image
tenant-id: {{adminTenentId}}

View File

@ -1,32 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.common;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
import cn.iocoder.yudao.module.system.service.common.CaptchaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 验证码")
@RestController
@RequestMapping("/system/captcha")
public class CaptchaController {
@Resource
private CaptchaService captchaService;
@GetMapping("/get-image")
@PermitAll
@ApiOperation("生成图片验证码")
public CommonResult<CaptchaImageRespVO> getCaptchaImage() {
return success(captchaService.getCaptchaImage());
}
}

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.system.controller.admin.common.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@ApiModel("管理后台 - 验证码图片 Response VO")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CaptchaImageRespVO {
@ApiModelProperty(value = "是否开启", required = true, example = "true", notes = "如果为 false则关闭验证码功能")
private Boolean enable;
@ApiModelProperty(value = "uuid", example = "1b3b7d00-83a8-4638-9e37-d67011855968",
notes = "enable = true 时,非空!通过该 uuid 作为该验证码的标识")
private String uuid;
@ApiModelProperty(value = "图片", notes = "enable = true 时,非空!验证码的图片内容,使用 Base64 编码")
private String img;
}

View File

@ -12,8 +12,8 @@ import java.util.Set;
@Data
public class PermissionAssignUserRoleReqVO {
@ApiModelProperty(value = "角色编号", required = true, example = "1")
@NotNull(message = "角色编号不能为空")
@ApiModelProperty(value = "用户编号", required = true, example = "1")
@NotNull(message = "用户编号不能为空")
private Long userId;
@ApiModelProperty(value = "角色编号列表", example = "1,3,5")

View File

@ -95,7 +95,7 @@ public class UserProfileController {
return success(true);
}
@PutMapping("/update-avatar")
@RequestMapping(value = "/update-avatar", method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题
@ApiOperation("上传用户个人头像")
public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws Exception {
if (file.isEmpty()) {

View File

@ -1,17 +0,0 @@
package cn.iocoder.yudao.module.system.convert.common;
import cn.hutool.captcha.AbstractCaptcha;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CaptchaConvert {
CaptchaConvert INSTANCE = Mappers.getMapper(CaptchaConvert.class);
default CaptchaImageRespVO convert(String uuid, AbstractCaptcha captcha) {
return CaptchaImageRespVO.builder().uuid(uuid).img(captcha.getImageBase64()).build();
}
}

View File

@ -1,9 +0,0 @@
package cn.iocoder.yudao.module.system.framework.captcha.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(CaptchaProperties.class)
public class CaptchaConfig {
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.system.framework.captcha.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.time.Duration;
@ConfigurationProperties(prefix = "yudao.captcha")
@Validated
@Data
public class CaptchaProperties {
private static final Boolean ENABLE_DEFAULT = true;
/**
* 是否开启
* 注意这里仅仅是后端 Server 是否校验暂时不控制前端的逻辑
*/
private Boolean enable = ENABLE_DEFAULT;
/**
* 验证码的过期时间
*/
@NotNull(message = "验证码的过期时间不为空")
private Duration timeout;
/**
* 验证码的高度
*/
@NotNull(message = "验证码的高度不能为空")
private Integer height;
/**
* 验证码的宽度
*/
@NotNull(message = "验证码的宽度不能为空")
private Integer width;
}

View File

@ -1,4 +0,0 @@
/**
* 基于 Hutool captcha 实现验证码功能
*/
package cn.iocoder.yudao.module.system.framework.captcha;

View File

@ -17,14 +17,17 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.service.common.CaptchaService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -47,8 +50,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Resource
private AdminUserService userService;
@Resource
private CaptchaService captchaService;
@Resource
private LoginLogService loginLogService;
@Resource
private OAuth2TokenService oauth2TokenService;
@ -56,13 +57,19 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private SocialUserService socialUserService;
@Resource
private MemberService memberService;
@Resource
private Validator validator;
@Resource
private CaptchaService captchaService;
@Resource
private SmsCodeApi smsCodeApi;
/**
* 验证码的开关默认为 true
*/
@Value("${yudao.captcha.enable:true}")
private Boolean captchaEnable;
@Override
public AdminUserDO authenticate(String username, String password) {
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
@ -86,7 +93,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override
public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
// 判断验证码是否正确
// 校验验证码
verifyCaptcha(reqVO);
// 使用账号密码进行登录
@ -97,7 +104,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
// 创建 Token 令牌记录登录日志
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
}
@ -127,32 +133,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
}
@VisibleForTesting
void verifyCaptcha(AuthLoginReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaService.isCaptchaEnable()) {
return;
}
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
// 验证码不存在
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
String code = captchaService.getCaptchaCode(reqVO.getUuid());
if (code == null) {
// 创建登录失败日志验证码不存在
createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND);
throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND);
}
// 验证码不正确
if (!code.equals(reqVO.getCode())) {
// 创建登录失败日志验证码不正确)
createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR);
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
}
// 正确所以要删除下验证码
captchaService.deleteCaptchaCode(reqVO.getUuid());
}
private void createLoginLog(Long userId, String username,
LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) {
// 插入登录日志
@ -197,6 +177,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return AuthConvert.INSTANCE.convert(accessTokenDO);
}
@VisibleForTesting
void verifyCaptcha(AuthLoginReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return;
}
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
ResponseModel response = captchaService.verification(captchaVO);
// 验证不通过
if (!response.isSuccess()) {
// 创建登录失败日志验证码不正确)
createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg());
}
}
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
// 插入登陆日志
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.system.service.common;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
/**
* 验证码 Service 接口
*/
public interface CaptchaService {
/**
* 获得验证码图片
*
* @return 验证码图片
*/
CaptchaImageRespVO getCaptchaImage();
/**
* 是否开启图片验证码
*
* @return 是否
*/
Boolean isCaptchaEnable();
/**
* 获得 uuid 对应的验证码
*
* @param uuid 验证码编号
* @return 验证码
*/
String getCaptchaCode(String uuid);
/**
* 删除 uuid 对应的验证码
*
* @param uuid 验证码编号
*/
void deleteCaptchaCode(String uuid);
}

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.module.system.service.common;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.module.system.convert.common.CaptchaConvert;
import cn.iocoder.yudao.module.system.framework.captcha.config.CaptchaProperties;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
import cn.iocoder.yudao.module.system.dal.redis.common.CaptchaRedisDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 验证码 Service 实现类
*/
@Service
public class CaptchaServiceImpl implements CaptchaService {
@Resource
private CaptchaProperties captchaProperties;
/**
* 验证码是否开关
*
* 虽然 {@link CaptchaProperties#getEnable()} 有该属性但是 Apollo Spring Boot 下无法刷新 @ConfigurationProperties 注解
* 所以暂时只能这么处理~
*/
@Value("${yudao.captcha.enable}")
private Boolean enable;
@Resource
private CaptchaRedisDAO captchaRedisDAO;
@Override
public CaptchaImageRespVO getCaptchaImage() {
if (!Boolean.TRUE.equals(enable)) {
return CaptchaImageRespVO.builder().enable(enable).build();
}
// 生成验证码
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
// 缓存到 Redis
String uuid = IdUtil.fastSimpleUUID();
captchaRedisDAO.set(uuid, captcha.getCode(), captchaProperties.getTimeout());
// 返回
return CaptchaConvert.INSTANCE.convert(uuid, captcha).setEnable(enable);
}
@Override
public Boolean isCaptchaEnable() {
return enable;
}
@Override
public String getCaptchaCode(String uuid) {
return captchaRedisDAO.get(uuid);
}
@Override
public void deleteCaptchaCode(String uuid) {
captchaRedisDAO.delete(uuid);
}
}

View File

@ -416,7 +416,7 @@ public class AdminUserServiceImpl implements AdminUserService {
AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
if (existUser == null) {
userMapper.insert(UserConvert.INSTANCE.convert(importUser)
.setPassword(encodePassword(userInitPassword))); // 设置默认密码
.setPassword(encodePassword(userInitPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组
respVO.getCreateUsernames().add(importUser.getUsername());
return;
}

View File

@ -11,13 +11,12 @@ import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.service.common.CaptchaService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import org.junit.jupiter.api.BeforeEach;
import com.anji.captcha.service.CaptchaService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
@ -57,11 +56,6 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
@MockBean
private Validator validator;
@BeforeEach
public void setUp() {
when(captchaService.isCaptchaEnable()).thenReturn(true);
}
@Test
public void testAuthenticate_success() {
// 准备参数
@ -138,82 +132,82 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
);
}
@Test
public void testCaptcha_success() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// @Test
// public void testCaptcha_success() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // mock 验证码正确
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
//
// // 调用
// authService.verifyCaptcha(reqVO);
// // 断言
// verify(captchaService).deleteCaptchaCode(reqVO.getUuid());
// }
//
// @Test
// public void testCaptcha_notFound() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // 调用, 并断言异常
// assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
// // 校验调用参数
// verify(loginLogService, times(1)).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
// );
// }
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// @Test
// public void testCaptcha_codeError() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
//
// // mock 验证码不正确
// String code = randomString();
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
//
// // 调用, 并断言异常
// assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
// // 校验调用参数
// verify(loginLogService).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
// );
// }
// 调用
authService.verifyCaptcha(reqVO);
// 断言
verify(captchaService).deleteCaptchaCode(reqVO.getUuid());
}
@Test
public void testCaptcha_notFound() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_NOT_FOUND);
// 校验调用参数
verify(loginLogService, times(1)).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult()))
);
}
@Test
public void testCaptcha_codeError() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码不正确
String code = randomString();
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code);
// 调用, 并断言异常
assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR);
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testLogin_success() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
o.setUsername("test_username").setPassword("test_password"));
// mock 验证码正确
when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
.setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
.thenReturn(accessTokenDO);
// 调用, 并断言异常
AuthLoginRespVO loginRespVO = authService.login(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
// @Test
// public void testLogin_success() {
// // 准备参数
// AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
// o.setUsername("test_username").setPassword("test_password"));
//
// // mock 验证码正确
// when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode());
// // mock user 数据
// AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username")
// .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus()));
// when(userService.getUserByUsername(eq("test_username"))).thenReturn(user);
// // mock password 匹配
// when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
// // mock 缓存登录用户到 Redis
// OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
// .setUserType(UserTypeEnum.ADMIN.getValue()));
// when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull()))
// .thenReturn(accessTokenDO);
//
// // 调用, 并断言异常
// AuthLoginRespVO loginRespVO = authService.login(reqVO);
// assertPojoEquals(accessTokenDO, loginRespVO);
// // 校验调用参数
// verify(loginLogService).createLoginLog(
// argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
// && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())
// && o.getUserId().equals(user.getId()))
// );
// }
@Test
public void testLogout_success() {

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.module.system.service.common;
import cn.iocoder.yudao.module.system.controller.admin.common.vo.CaptchaImageRespVO;
import cn.iocoder.yudao.module.system.dal.redis.common.CaptchaRedisDAO;
import cn.iocoder.yudao.module.system.framework.captcha.config.CaptchaProperties;
import cn.iocoder.yudao.framework.test.core.ut.BaseRedisUnitTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
@Import({CaptchaServiceImpl.class, CaptchaProperties.class, CaptchaRedisDAO.class})
public class CaptchaServiceTest extends BaseRedisUnitTest {
@Resource
private CaptchaServiceImpl captchaService;
@Resource
private CaptchaRedisDAO captchaRedisDAO;
@Resource
private CaptchaProperties captchaProperties;
@Test
public void testGetCaptchaImage() {
// 调用
CaptchaImageRespVO respVO = captchaService.getCaptchaImage();
// 断言
assertNotNull(respVO.getUuid());
assertNotNull(respVO.getImg());
String captchaCode = captchaRedisDAO.get(respVO.getUuid());
assertNotNull(captchaCode);
}
@Test
public void testGetCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
String resultCode = captchaService.getCaptchaCode(uuid);
// 断言
assertEquals(code, resultCode);
}
@Test
public void testDeleteCaptchaCode() {
// 准备参数
String uuid = randomString();
String code = randomString();
// mock 数据
captchaRedisDAO.set(uuid, code, captchaProperties.getTimeout());
// 调用
captchaService.deleteCaptchaCode(uuid);
// 断言
assertNull(captchaRedisDAO.get(uuid));
}
}

View File

@ -68,6 +68,11 @@
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
</dependency>
<!-- 单独依赖升级版本解决低版本validator失败问题 -->
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -167,6 +167,13 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
appid: wx63c280fe3248a3e7
secret: 6f270509224a7ae1296bbf1c8cb97aed
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
--- #################### 芋道相关配置 ####################

View File

@ -46,25 +46,25 @@ spring:
master:
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
slave: # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:

View File

@ -57,6 +57,25 @@ mybatis-plus:
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
--- #################### 验证码相关配置 ####################
aj:
captcha:
jigsaw: classpath:images/jigsaw # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径
pic-click: classpath:images/pic-click # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径
cache-type: redis # 缓存 local/redis...
cache-number: 1000 # local 缓存的阈值,达到这个值,清除缓存
timing-clear: 180 # local定时清除过期缓存(单位秒),设置为0代表不执行
type: blockPuzzle # 验证码类型 default两种都实例化。 blockPuzzle 滑块拼图 clickWord 文字点选
water-mark: 芋道源码 # 右下角水印文字(我的水印),可使用 https://tool.chinaz.com/tools/unicode.aspx 中文转 UnicodeLinux 可能需要转 unicode
interference-options: 2 # 滑动干扰项(0/1/2)
req-frequency-limit-enable: false # 接口请求次数一分钟限制是否开启 true|false
req-get-lock-limit: 5 # 验证失败5次get接口锁定
req-get-lock-seconds: 10 # 验证失败后,锁定时间间隔
req-get-minute-limit: 30 # get 接口一分钟内请求数限制
req-check-minute-limit: 60 # check 接口一分钟内请求数限制
req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制
--- #################### 芋道相关配置 ####################
yudao:
@ -75,9 +94,7 @@ yudao:
version: ${yudao.info.version}
base-package: ${yudao.info.base-package}
captcha:
timeout: 5m
width: 160
height: 60
enable: true # 验证码的开关,默认为 true注意优先读取数据库 infra_config 的 yudao.captcha.enable所以请从数据库修改可能需要重启项目
codegen:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
@ -92,12 +109,12 @@ yudao:
enable: true
ignore-urls:
- /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
- /admin-api/system/captcha/get-image # 获取图片验证码,和租户无关
- /captcha/get # 获取图片验证码,和租户无关
- /captcha/check # 校验图片验证码,和租户无关
- /admin-api/infra/file/*/get/** # 获取图片,和租户无关
- /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
- /app-api/pay/order/notify/* # 支付回调通知,不携带租户编号
# - /jmreport/list
- /jmreport/*
- /jmreport/* # 积木报表,无法携带租户编号
ignore-tables:
- system_tenant
- system_tenant_package

View File

@ -50,7 +50,12 @@ public class ProjectReactor {
String projectBaseDirNew = projectBaseDir + "-new"; // 一键改名后项目所在的目录
log.info("[main][检测新项目目录 ({})是否存在]", projectBaseDirNew);
if (FileUtil.exist(projectBaseDirNew)) {
log.info("[main][新项目目录检测 ({})已存在,请更改新的目录,程序退出]", projectBaseDirNew);
log.error("[main][新项目目录检测 ({})已存在,请更改新的目录!程序退出]", projectBaseDirNew);
return;
}
// 如果新目录中存在 PACKAGE_NAMEARTIFACT_ID 等关键字路径会被替换导致生成的文件不在预期目录
if (StrUtil.containsAny(projectBaseDirNew, PACKAGE_NAME, ARTIFACT_ID, StrUtil.upperFirst(ARTIFACT_ID))) {
log.error("[main][新项目目录检测 ({}) 存在冲突名称「{}」或者「{}」,请更改新的目录!程序退出]", projectBaseDirNew, PACKAGE_NAME, ARTIFACT_ID);
return;
}
log.info("[main][完成新项目目录检测,新项目路径地址 ({})]", projectBaseDirNew);

16
yudao-ui-admin-uniapp/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
######################################################################
# Build Tools
/unpackage/*
/node_modules/*
######################################################################
# Development Tools
/.idea/*
/.vscode/*
/.hbuilderx/*
package-lock.json
yarn.lock

View File

@ -0,0 +1,34 @@
<script>
import config from './config'
import store from '@/store'
import { getAccessToken } from '@/utils/auth'
export default {
onLaunch: function() {
this.initApp()
},
methods: {
//
initApp() {
//
this.initConfig()
//
//#ifdef H5
this.checkLogin()
//#endif
},
initConfig() {
this.globalData.config = config
},
checkLogin() {
if (!getAccessToken()) {
this.$tab.reLaunch('/pages/login')
}
}
}
}
</script>
<style lang="scss">
@import '@/static/scss/index.scss'
</style>

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 芋道
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 登录方法
export function login(username, password, captchaVerification) {
const data = {
username,
password,
captchaVerification
}
return request({
url: '/system/auth/login',
headers: {
isToken: false
},
'method': 'POST',
'data': data
})
}
// 获取用户详细信息
export function getInfo() {
return request({
url: '/system/auth/get-permission-info',
'method': 'GET'
})
}
// 退出方法
export function logout() {
return request({
url: '/system/auth/logout',
'method': 'POST'
})
}
// 获取验证码
export function getCaptcha(data) {
return request({
url: '/captcha/get',
headers: {
isToken: false,
isTenant: false
},
method: 'POST',
'data': data
})
}
// 验证验证码
export function checkCaptcha(data) {
return request({
url: '/captcha/check',
headers: {
isToken: false,
isTenant: false
},
method: 'POST',
'data': data
})
}

View File

@ -0,0 +1,42 @@
import upload from '@/utils/upload'
import request from '@/utils/request'
// 用户密码重置
export function updateUserPwd(oldPassword, newPassword) {
const data = {
oldPassword,
newPassword
}
return request({
url: '/system/user/profile/update-password',
method: 'PUT',
params: data
})
}
// 查询用户个人信息
export function getUserProfile() {
return request({
url: '/system/user/profile/get',
method: 'GET'
})
}
// 修改用户个人信息
export function updateUserProfile(data) {
return request({
url: '/system/user/profile/update',
method: 'PUT',
data: data
})
}
// 用户头像上传
export function uploadAvatar(data) {
return upload({
url: '/system/user/profile/update-avatar',
method: 'PUT',
name: data.name,
filePath: data.filePath
})
}

View File

@ -0,0 +1,167 @@
<template>
<view class="uni-section">
<view class="uni-section-header" @click="onClick">
<view class="uni-section-header__decoration" v-if="type" :class="type" />
<slot v-else name="decoration"></slot>
<view class="uni-section-header__content">
<text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text>
<text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text>
</view>
<view class="uni-section-header__slot-right">
<slot name="right"></slot>
</view>
</view>
<view class="uni-section-content" :style="{padding: _padding}">
<slot />
</view>
</view>
</template>
<script>
/**
* Section 标题栏
* @description 标题栏
* @property {String} type = [line|circle|square] 标题装饰类型
* @value line 竖线
* @value circle 圆形
* @value square 正方形
* @property {String} title 主标题
* @property {String} titleFontSize 主标题字体大小
* @property {String} titleColor 主标题字体颜色
* @property {String} subTitle 副标题
* @property {String} subTitleFontSize 副标题字体大小
* @property {String} subTitleColor 副标题字体颜色
* @property {String} padding 默认插槽 padding
*/
export default {
name: 'UniSection',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
title: {
type: String,
required: true,
default: ''
},
titleFontSize: {
type: String,
default: '14px'
},
titleColor:{
type: String,
default: '#333'
},
subTitle: {
type: String,
default: ''
},
subTitleFontSize: {
type: String,
default: '12px'
},
subTitleColor: {
type: String,
default: '#999'
},
padding: {
type: [Boolean, String],
default: false
}
},
computed:{
_padding(){
if(typeof this.padding === 'string'){
return this.padding
}
return this.padding?'10px':''
}
},
watch: {
title(newVal) {
if (uni.report && newVal !== '') {
uni.report('title', newVal)
}
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
.uni-section {
background-color: #fff;
.uni-section-header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 12px 10px;
font-weight: normal;
&__decoration{
margin-right: 6px;
background-color: $uni-primary;
&.line {
width: 4px;
height: 12px;
border-radius: 10px;
}
&.circle {
width: 8px;
height: 8px;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
}
&.square {
width: 8px;
height: 8px;
}
}
&__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
color: #333;
.distraction {
flex-direction: row;
align-items: center;
}
&-sub {
margin-top: 2px;
}
}
&__slot-right{
font-size: 14px;
}
}
.uni-section-content{
font-size: 14px;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
import CryptoJS from 'crypto-js'
/**
* @word 要加密的内容
* @keyWord String 服务器随机返回的关键字
* */
export function aesEncrypt(word, keyWord = "XwKsGlMcdPMEhR1B") {
var key = CryptoJS.enc.Utf8.parse(keyWord);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}

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