diff --git a/README.md b/README.md index 0c8ccac05..7b2080bab 100644 --- a/README.md +++ b/README.md @@ -170,28 +170,28 @@ 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 前端](./yudao-ui-admin) @@ -202,16 +202,16 @@ ps:核心功能已经实现,正在对接微信小程序中... ### [管理后台 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/) | Vue 存储库 替代 vuex5 | 2.0.17 | -| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.0 | -| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 | -| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.1 | +| 框架 | 说明 | 版本 | +|----------------------------------------------------------------------|-----------------|--------| +| [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) diff --git a/sql/mysql/update.sql b/sql/mysql/update.sql new file mode 100644 index 000000000..10df65590 --- /dev/null +++ b/sql/mysql/update.sql @@ -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'); \ No newline at end of file diff --git a/sql/optional/mall/coupon.sql b/sql/optional/mall/coupon.sql new file mode 100644 index 000000000..4ab7b1ff9 --- /dev/null +++ b/sql/optional/mall/coupon.sql @@ -0,0 +1,85 @@ +DROP TABLE IF EXISTS `coupon`; +CREATE TABLE `coupon` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券类型 reward-满减 discount-折扣 random-随机', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券名称', + `coupon_type_id` bigint UNSIGNED DEFAULT 0 COMMENT '优惠券类型id', + `coupon_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券编码', + `member_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '领用人', + `use_order_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠券使用订单id', + `goods_type` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用', + `goods_ids` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '适用商品id', + `at_least` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最小金额', + `money` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '面额', + `discount` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '1 =< 折扣 <= 9.9 当type为discount时需要添加', + `discount_limit` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最多折扣金额 当type为discount时可选择性添加', + `whether_forbid_preference` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用', + `whether_expire_notice` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启过期提醒0-不开启 1-开启', + `expire_notice_fixed_term` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '过期前N天提醒', + `whether_noticed` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否已提醒', + `state` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠券状态 1已领用(未使用) 2已使用 3已过期', + `get_type` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取', + `fetch_time` datetime NOT NULL DEFAULT 0 COMMENT '领取时间', + `use_time` datetime NOT NULL DEFAULT 0 COMMENT '使用时间', + `start_time` datetime NOT NULL DEFAULT 0 COMMENT '可使用的开始时间', + `end_time` datetime NOT NULL DEFAULT 0 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 = 119 + CHARACTER SET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = '优惠券'; + +DROP TABLE IF EXISTS `coupon_templete`; +CREATE TABLE `coupon_templete` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券类型 reward-满减 discount-折扣 random-随机', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠券名称', + `coupon_name_remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '名称备注', + `image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '优惠券图片', + `count` int(11) NOT NULL DEFAULT 0 COMMENT '发放数量', + `lead_count` int(11) NOT NULL DEFAULT 0 COMMENT '已领取数量', + `used_count` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '已使用数量', + `goods_type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用', + `product_ids` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '适用商品id', + `has_use_limit` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用门槛0-无门槛 1-有门槛', + `at_least` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '满多少元使用 0代表无限制', + `money` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '发放面额 当type为reward时需要添加', + `discount` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '1 =< 折扣 <= 9.9 当type为discount时需要添加', + `discount_limit` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '最多折扣金额 当type为discount时可选择性添加', + `min_money` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最低金额 当type为radom时需要添加', + `max_money` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大金额 当type为radom时需要添加', + `validity_type` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期', + `start_use_time` datetime COMMENT '使用开始日期 过期类型1时必填', + `end_use_time` datetime COMMENT '使用结束日期 过期类型1时必填', + `fixed_term` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效', + `whether_limitless` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否无限制0-否 1是', + `max_fetch` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '每人最大领取个数', + `whether_expire_notice` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启过期提醒0-不开启 1-开启', + `expire_notice_fixed_term` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '过期前N天提醒', + `whether_forbid_preference` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用', + `whether_show` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否显示', + `discount_order_money` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单的优惠总金额', + `order_money` decimal(10, 2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用券总成交额', + `whether_forbidden` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否禁止发放0-否 1-是', + `order_goods_num` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用优惠券购买的商品数量', + `status` tinyint(11) NOT NULL DEFAULT 0 COMMENT '状态(1进行中2已结束-1已关闭)', + `end_time` datetime 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 = 119 + CHARACTER SET = utf8mb4 + COLLATE = utf8mb4_unicode_ci COMMENT = '优惠券模板'; \ No newline at end of file diff --git a/sql/optional/mall/mall.sql b/sql/optional/mall/mall.sql index 9a090c9e4..486b6f2fc 100644 --- a/sql/optional/mall/mall.sql +++ b/sql/optional/mall/mall.sql @@ -1,7 +1,7 @@ /* Navicat Premium Data Transfer - Source Server : 127.0.0.1 + Source Server : 127.0.0.1 MySQL Source Server Type : MySQL Source Server Version : 80026 Source Host : localhost:3306 @@ -11,277 +11,305 @@ Target Server Version : 80026 File Encoding : 65001 - Date: 05/02/2022 00:50:30 + Date: 01/08/2022 23:01:36 */ -SET -FOREIGN_KEY_CHECKS = 0; + SET NAMES utf8mb4; - --- ---------------------------- --- Table structure for product_category --- ---------------------------- -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) NOT NULL COMMENT '分类名称', - `icon` varchar(100) NOT NULL DEFAULT '#' COMMENT '分类图标', - `banner_url` varchar(255) NOT NULL COMMENT '分类图片', - `sort` int DEFAULT '0' COMMENT '分类排序', - `description` varchar(1024) DEFAULT NULL COMMENT '分类描述', - `status` tinyint NOT NULL COMMENT '开启状态', - `creator` varchar(64) DEFAULT '' COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updater` varchar(64) 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 COMMENT='商品分类'; - --- ---------------------------- --- Table structure for product_brand --- ---------------------------- -DROP TABLE IF EXISTS `product_brand`; -CREATE TABLE `product_brand` -( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '品牌编号', - `category_id` bigint NOT NULL COMMENT '分类编号', - `name` varchar(255) NOT NULL COMMENT '品牌名称', - `banner_url` varchar(255) NOT NULL COMMENT '品牌图片', - `sort` int DEFAULT '0' COMMENT '品牌排序', - `description` varchar(1024) DEFAULT NULL COMMENT '品牌描述', - `status` tinyint NOT NULL COMMENT '状态', - `creator` varchar(64) DEFAULT '' COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updater` varchar(64) 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 COMMENT='品牌'; - --- TODO 父级菜单的 id 处理: 2000 、 2001 -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES (2000, '商城', '', 1, 1, 0, '/mall', 'merchant', NULL, 0); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES (2001, '商品', '', 1, 1, 2000, 'product', 'dict', NULL, 0); --- 商品分类 菜单 SQL -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类管理', '', 2, 0, 2001, 'category', '', 'mall/product/category/index', 0); --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); --- 按钮 SQL -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类查询', 'product:category:query', 3, 1, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类创建', 'product:category:create', 3, 2, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类更新', 'product:category:update', 3, 3, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类删除', 'product:category:delete', 3, 4, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('分类导出', 'product:category:export', 3, 5, @parentId, '', '', '', 0); --- 品牌管理 菜单 SQL -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌管理', '', 2, 1, 2001, 'brand', '', 'mall/product/brand/index', 0); --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); --- 按钮 SQL -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌查询', 'product:brand:query', 3, 1, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌创建', 'product:brand:create', 3, 2, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌更新', 'product:brand:update', 3, 3, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌删除', 'product:brand:delete', 3, 4, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('品牌导出', 'product:brand:export', 3, 5, @parentId, '', '', '', 0); - +SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for market_activity -- ---------------------------- DROP TABLE IF EXISTS `market_activity`; -CREATE TABLE `market_activity` -( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '活动编号', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '活动标题', - `activity_type` tinyint(4) NOT NULL COMMENT '活动类型', - `status` tinyint(4) NOT NULL DEFAULT '-1' COMMENT '活动状态', - `start_time` datetime NOT NULL COMMENT '开始时间', - `end_time` datetime NOT NULL COMMENT '结束时间', - `invalid_time` datetime DEFAULT NULL COMMENT '失效时间', - `delete_time` datetime DEFAULT NULL COMMENT '删除时间', - `time_limited_discount` varchar(2000) DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储', - `full_privilege` varchar(2000) DEFAULT NULL COMMENT '限制折扣字符串,使用 JSON 序列化成字符串存储', - `creator` varchar(64) DEFAULT '' COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updater` varchar(64) 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 DEFAULT CHARSET=utf8mb4 COMMENT='促销活动'; +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 +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '促销活动'; +-- ---------------------------- +-- Records of market_activity +-- ---------------------------- +BEGIN; +COMMIT; --- 规格菜单 SQL -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格管理', '', 2, 3, 2001, 'property', '', 'mall/product/property/index', 0); +-- ---------------------------- +-- Table structure for market_banner +-- ---------------------------- +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 +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Banner管理'; --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); +-- ---------------------------- +-- Records of market_banner +-- ---------------------------- +BEGIN; +COMMIT; --- 按钮 SQL -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格查询', 'product:property:query', 3, 1, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格创建', 'product:property:create', 3, 2, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格更新', 'product:property:update', 3, 3, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格删除', 'product:property:delete', 3, 4, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('规格导出', 'product:property:export', 3, 5, @parentId, '', '', '', 0); +-- ---------------------------- +-- Table structure for member_address +-- ---------------------------- +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 +) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '用户收件地址'; +-- ---------------------------- +-- Records of member_address +-- ---------------------------- +BEGIN; +INSERT INTO `member_address` (`id`, `user_id`, `name`, `mobile`, `area_id`, `post_code`, `detail_address`, `defaulted`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (21, 1, 'yunai', '15601691300', 610632, '200000', '芋道源码 233 号 666 室', b'1', '1', '2022-08-01 22:46:35', '1', '2022-08-01 22:46:35', b'0', 1); +COMMIT; --- 商品菜单 SQL -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品管理', '', 2, 2, 2001, 'spu', '', 'mall/product/spu/index', 0); +-- ---------------------------- +-- Table structure for product_brand +-- ---------------------------- +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 +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品品牌'; --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); +-- ---------------------------- +-- Records of product_brand +-- ---------------------------- +BEGIN; +INSERT INTO `product_brand` (`id`, `name`, `pic_url`, `sort`, `description`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '苹果', 'http://test.yudao.iocoder.cn/e3726713fa56db5717c78c011762fcc7a251db12735c3581470638b8e1fa17e2.jpeg', 0, '是上市', 0, '1', '2022-07-30 22:12:18', '1', '2022-07-30 22:13:55', b'0', 1); +COMMIT; --- 按钮 SQL -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品查询', 'product:spu:query', 3, 1, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品创建', 'product:spu:create', 3, 2, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品更新', 'product:spu:update', 3, 3, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品删除', 'product:spu:delete', 3, 4, @parentId, '', '', '', 0); -INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) -VALUES ('商品导出', 'product:spu:export', 3, 5, @parentId, '', '', '', 0); +-- ---------------------------- +-- Table structure for product_category +-- ---------------------------- +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 +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商品分类'; +-- ---------------------------- +-- Records of product_category +-- ---------------------------- +BEGIN; +INSERT INTO `product_category` (`id`, `parent_id`, `name`, `pic_url`, `sort`, `description`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 0, '电脑办公', 'http://test.yudao.iocoder.cn/122d548e1b3cd5dec72fe8075c6977a70f9cc13541a684ab3685f1b5df42f6bd.jpeg', 1, '1234', 0, '1', '2022-07-30 16:36:35', '1', '2022-07-30 20:27:16', b'0', 1), (2, 1, '笔记本', 'http://test.yudao.iocoder.cn/72713ac7b947600a019a18786ed0e6562e8692e253dbd35110a0a85c2469bbec.jpg', 1, '

测试一下

', 0, '1', '2022-07-30 16:38:09', '1', '2022-07-30 16:38:09', b'0', 1), (3, 1, '游戏本', 'http://test.yudao.iocoder.cn/287c50dd9f5f575f57329a0c57b2095be6d1aeba83867b905fe549f54a296feb.jpg', 2, '

测试一下

', 0, '1', '2022-07-30 16:39:09', '1', '2022-07-30 20:26:59', b'0', 1), (4, 0, '手机', 'http://test.yudao.iocoder.cn/e1b63900c78dbb661b3e383960cee5cfea7e1dd2fb22cff2e317ff025faaf8b2.jpeg', 2, '

123

', 0, '1', '2022-07-30 16:40:00', '1', '2022-07-30 16:40:09', b'0', 1), (5, 4, '5G手机', 'http://test.yudao.iocoder.cn/3af6557ac7def6423f046f5b2e920b644793420b466959aaa996a2e19068bbde.jpeg', 1, '


', 0, '1', '2022-07-30 16:43:00', '1', '2022-07-30 16:43:00', b'0', 1), (6, 4, '游戏手机', 'http://test.yudao.iocoder.cn/964fe9ccd1710d64ede261dc36d231918a017641986c15293c367f9f66d94d05.jpeg', 2, NULL, 0, '1', '2022-07-30 16:43:44', '1', '2022-07-30 16:43:44', b'0', 1), (7, 5, '厉害的 5G 手机', 'http://test.yudao.iocoder.cn/b287122f277838e8de368769b96217918605743bc45f3a29bda3cc7359dc66e1.png', 0, '123', 0, '1', '2022-07-30 20:38:09', '1', '2022-07-30 20:38:09', b'0', 1); +COMMIT; --- 规格名称表 -drop table if exists product_property; -create table product_property -( - id bigint NOT NULL AUTO_INCREMENT comment '主键', - name varchar(64) comment '规格名称', - status tinyint comment '状态: 0 开启 ,1 禁用', - create_time datetime default current_timestamp comment '创建时间', - update_time datetime default current_timestamp on update current_timestamp comment '更新时间', - creator varchar(64) comment '创建人', - updater varchar(64) comment '更新人', - tenant_id bigint NOT NULL DEFAULT '0' COMMENT '租户编号', - deleted bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - primary key (id), - key idx_name ( name (32)) comment '规格名称索引' -) comment '规格名称' character set utf8mb4 - collate utf8mb4_general_ci; +-- ---------------------------- +-- Table structure for product_property +-- ---------------------------- +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 '规格名称索引' +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格名称'; --- 规格值表 -drop table if exists product_property_value; -create table product_property_value -( - id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', - property_id bigint comment '规格键id', - name varchar(128) comment '规格值名字', - status tinyint comment '状态: 1 开启 ,2 禁用', - create_time datetime default current_timestamp comment '创建时间', - update_time datetime default current_timestamp on update current_timestamp comment '更新时间', - creator varchar(64) comment '创建人', - updater varchar(64) comment '更新人', - tenant_id bigint NOT NULL DEFAULT '0' COMMENT '租户编号', - deleted bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - primary key (id) -) comment '规格值' character set utf8mb4 - collate utf8mb4_general_ci; +-- ---------------------------- +-- Records of product_property +-- ---------------------------- +BEGIN; +COMMIT; --- spu -drop table if exists product_spu; -create table product_spu -( - id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', - name varchar(128) comment '商品名称', - sell_point varchar(128) not null comment '卖点', - description text not null comment '描述', - category_id bigint not null comment '分类id', - pic_urls varchar(1024) not null default '' comment '商品主图地址\n *\n * 数组,以逗号分隔\n 最多上传15张', - sort int not null default 0 comment '排序字段', - like_count int comment '点赞初始人数', - price int comment '价格 单位使用:分', - quantity int comment '库存数量', - status bit(1) comment '上下架状态: 0 上架(开启) 1 下架(禁用)', - create_time datetime default current_timestamp comment '创建时间', - update_time datetime default current_timestamp on update current_timestamp comment '更新时间', - creator varchar(64) comment '创建人', - updater varchar(64) comment '更新人', - tenant_id bigint NOT NULL DEFAULT '0' COMMENT '租户编号', - deleted bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - primary key (id) -) comment '商品spu' character set utf8mb4 - collate utf8mb4_general_ci; +-- ---------------------------- +-- Table structure for product_property_value +-- ---------------------------- +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 +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '规格值'; +-- ---------------------------- +-- Records of product_property_value +-- ---------------------------- +BEGIN; +COMMIT; --- sku -drop table if exists product_sku; -create table product_sku -( - id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', - spu_id bigint not null comment 'spu编号', - properties varchar(64) not null comment '规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]', - price int not null DEFAULT -1 comment '销售价格,单位:分', - original_price int not null DEFAULT -1 comment '原价, 单位: 分', - cost_price int not null DEFAULT -1 comment '成本价,单位: 分', - bar_code varchar(64) not null comment '条形码', - pic_url VARCHAR(128) not null comment '图片地址', - status tinyint comment '状态: 0-正常 1-禁用', - create_time datetime default current_timestamp comment '创建时间', - update_time datetime default current_timestamp on update current_timestamp comment '更新时间', - creator varchar(64) comment '创建人', - updater varchar(64) comment '更新人', - tenant_id bigint NOT NULL DEFAULT '0' COMMENT '租户编号', - deleted bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - primary key (id) -) comment '商品sku' character set utf8mb4 - collate utf8mb4_general_ci; +-- ---------------------------- +-- Table structure for product_sku +-- ---------------------------- +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 '更新人', +`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', +PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB COMMENT='商品sku'; +-- ---------------------------- +-- Records of product_sku +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for product_spu +-- ---------------------------- +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 +) ENGINE=InnoDB COMMENT='商品spu'; + +-- ---------------------------- +-- Records of product_spu +-- ---------------------------- +BEGIN; +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; + +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, '商品中心', '', 1, 60, 0, '/product', 'merchant', NULL, 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:26:19', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, '商品分类', '', 2, 2, 2000, 'category', 'dict', 'mall/product/category/index', 0, b'1', b'1', '', '2022-07-29 15:53:53', '1', '2022-07-30 22:23:37', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, '分类查询', 'product:category:query', 3, 1, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, '分类创建', 'product:category:create', 3, 2, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, '分类更新', 'product:category:update', 3, 3, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, '分类删除', 'product:category:delete', 3, 4, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-29 15:53:53', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2007, '分类导出', 'product:category:export', 3, 5, 2002, '', '', '', 0, b'1', b'1', '', '2022-07-29 15:53:53', '', '2022-07-30 13:52:13', b'1'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, '商品品牌', '', 2, 1, 2000, 'brand', 'dashboard', 'mall/product/brand/index', 0, b'1', b'1', '', '2022-07-30 13:52:44', '1', '2022-07-30 22:23:43', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, '品牌查询', 'product:brand:query', 3, 1, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, '品牌创建', 'product:brand:create', 3, 2, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, '品牌更新', 'product:brand:update', 3, 3, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, '品牌删除', 'product:brand:delete', 3, 4, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 13:52:44', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2013, '品牌导出', 'product:brand:export', 3, 5, 2008, '', '', '', 0, b'1', b'1', '', '2022-07-30 13:52:44', '', '2022-07-30 14:15:00', b'1'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, '商品管理', '', 2, 0, 2000, 'spu', 'link', 'mall/product/spu/index', 0, b'1', b'1', '', '2022-07-30 14:22:58', '1', '2022-07-30 22:26:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, '商品查询', 'product:spu:query', 3, 1, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, '商品创建', 'product:spu:create', 3, 2, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, '商品更新', 'product:spu:update', 3, 3, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, '商品删除', 'product:spu:delete', 3, 4, 2014, '', '', '', 0, b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, '规格管理', '', 2, 3, 2000, 'property', '', 'mall/product/property/index', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, '规格查询', 'product:property:query', 3, 1, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, '规格创建', 'product:property:create', 3, 2, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, '规格更新', 'product:property:update', 3, 3, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, '规格删除', 'product:property:delete', 3, 4, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, '规格导出', 'product:property:export', 3, 5, 2019, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:55:35', '', '2022-08-01 14:55:35', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 'Banner管理', '', 2, 1, 2000, 'brand', '', 'mall/market/banner/index', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 'Banner查询', 'market:banner:query', 3, 1, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 'Banner创建', 'market:banner:create', 3, 2, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 'Banner更新', 'market:banner:update', 3, 3, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); +INSERT INTO `ruoyi-vue-pro`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 'Banner删除', 'market:banner:delete', 3, 4, 2025, '', '', '', 0, b'1', b'1', '', '2022-08-01 14:56:14', '', '2022-08-01 14:56:14', b'0'); --- Market-Banner管理SQL -drop table if exists market_banner; -CREATE TABLE `market_banner` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Banner编号', - `title` varchar(64) NOT NULL DEFAULT '' COMMENT 'Banner标题', - `pic_url` varchar(255) NOT NULL COMMENT '图片URL', - `status` tinyint(4) NOT NULL DEFAULT '-1' COMMENT '活动状态', - `url` varchar(255) NOT NULL COMMENT '跳转地址', - `creator` varchar(64) DEFAULT '' COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updater` varchar(64) 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(20) NOT NULL DEFAULT '0' COMMENT '租户编号', - `sort` tinyint(4) DEFAULT NULL COMMENT '排序', - `memo` varchar(255) DEFAULT NULL COMMENT '描述', - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='Banner管理'; --- 菜单 SQL -INSERT INTO `system_menu`(`id`,`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES (2026, 'Banner管理', '', 2, 1, 2000, 'brand', '', 'mall/market/banner/index', 0); --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); --- 按钮 SQL -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('Banner查询', 'market:banner:query', 3, 1, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('Banner创建', 'market:banner:create', 3, 2, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('Banner更新', 'market:banner:update', 3, 3, @parentId, '', '', '', 0); -INSERT INTO `system_menu`(`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) -VALUES ('Banner删除', 'market:banner:delete', 3, 4, @parentId, '', '', '', 0); diff --git a/sql/optional/mall/order.sql b/sql/optional/mall/order.sql new file mode 100644 index 000000000..21e60200d --- /dev/null +++ b/sql/optional/mall/order.sql @@ -0,0 +1,76 @@ +/**todo cancelType 设置默认值 0?*/ +CREATE TABLE `trade_order` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `sn` varchar(32) NOT NULL COMMENT '订单流水号', + `type` int NOT NULL DEFAULT '0' COMMENT '订单类型:[0:普通订单 1:秒杀订单 2:拼团订单 3:砍价订单]', + `terminal` int NOT NULL COMMENT '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]', + `user_id` bigint unsigned NOT NULL COMMENT '用户编号', + `user_ip` varchar(30) NOT NULL DEFAULT '' COMMENT '用户 IP', + `user_remark` varchar(200) DEFAULT NULL COMMENT '用户备注', + `status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:待付款 1:待发货 2:待收货 3:已完成 4:已关闭]', + `product_count` int NOT NULL COMMENT '购买的商品数量', + `cancel_type` int NOT NULL COMMENT '取消类型:[10:超时未支付 20:退款关闭 30:买家取消 40:已通过货到付款交易]', + `remark` varchar(200) DEFAULT NULL COMMENT '商家备注', + `payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]', + `finish_time` datetime DEFAULT NULL COMMENT '订单完成时间', + `cancel_time` datetime DEFAULT NULL COMMENT '订单取消时间', + `sku_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分', + `sku_promotion_price` int NOT NULL DEFAULT '0' COMMENT '商品优惠(总),单位:分', + `order_promotion_price` int NOT NULL DEFAULT '0' COMMENT '订单优惠(总),单位:分', + `delivery_price` int NOT NULL DEFAULT '0' COMMENT '运费金额,单位:分', + `pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分', + `pay_order_id` int NOT NULL COMMENT '支付订单编号', + `pay_channel` int NOT NULL COMMENT '支付成功的支付渠道', + `delivery_type` int NOT NULL DEFAULT '1' COMMENT '配送方式:[1:快递发货 2:自提]', + `actual_delivery_type` int NOT NULL DEFAULT '1' COMMENT '实际的配送方式:[1:快递发货 2:自提]', + `delivery_templateid` int DEFAULT NULL COMMENT '配置模板的编号', + `express_no` int DEFAULT NULL COMMENT '物流公司单号', + `delivery_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '发货状态[0:未发货 1:已发货]', + `delivery_time` datetime DEFAULT NULL COMMENT '发货时间', + `receive_time` datetime DEFAULT NULL COMMENT '收货时间', + `receiver_name` varchar(20) NOT NULL COMMENT '收件人名称', + `receiver_mobile` varchar(20) NOT NULL COMMENT '收件人手机', + `receiver_area_id` int NOT NULL COMMENT '收件人地区编号', + `receiver_post_code` int DEFAULT NULL COMMENT '收件人邮编', + `receiver_detail_address` varchar(255) NOT NULL COMMENT '收件人详细地址', + `refund_status` int NOT NULL DEFAULT '0' COMMENT '订单状态:[0:未退款 1:部分退款 2:全部退款]', + `refund_price` int NOT NULL DEFAULT '0' COMMENT '退款金额,单位:分', + `coupon_id` bigint unsigned NOT NULL COMMENT '优惠劵编号', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='交易订单表'; + + +DROP TABLE IF EXISTS `trade_order_item`; +CREATE TABLE `trade_order_item` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `user_id` bigint unsigned NOT NULL COMMENT '用户编号', + `order_Id` bigint unsigned NOT NULL COMMENT '订单编号', + `spu_id` bigint unsigned NOT NULL COMMENT '商品 SPU 编号', + `sku_id` bigint unsigned NOT NULL COMMENT '商品 SKU 编号', + `properties` json DEFAULT NULL COMMENT '规格值数组,JSON 格式', + `name` varchar(128) NOT NULL DEFAULT '' COMMENT '商品名称', + `pic_url` varchar(200) DEFAULT NULL COMMENT '商品图片', + `count` int NOT NULL COMMENT '购买数量', + `commented` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否评论:[0:未评论 1:已评论]', + `original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(单),单位:分', + `total_original_price` int NOT NULL DEFAULT '0' COMMENT '商品原价(总),单位:分', + `total_promotion_price` int NOT NULL DEFAULT '0' COMMENT '商品级优惠(总),单位:分', + `present_price` int NOT NULL DEFAULT '0' COMMENT '最终购买金额(单),单位:分。', + `total_present_price` int NOT NULL DEFAULT '0' COMMENT '最终购买金额(总),单位:分。', + `total_pay_price` int NOT NULL DEFAULT '0' COMMENT '应付金额(总),单位:分', + `refund_status` int NOT NULL DEFAULT '0' COMMENT '退款状态:[0:未申请退款 1:申请退款 2:等待退款 3:退款成功]', + `refund_total` int NOT NULL DEFAULT '0' COMMENT '退款总金额,单位:分', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='交易订单明细表'; \ No newline at end of file diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 44d7bdf68..d7912ba69 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -41,14 +41,14 @@ 0.1.16 4.0.0 - 6.7.0 + 6.7.2 3.0.4 1.18.20 1.4.1.Final - 5.7.22 + 5.8.5 3.1.1 - 2.2 + 2.3 1.0.5 1.2.83 30.1.1-jre @@ -57,11 +57,12 @@ 3.8.0 0.1.55 2.4.1 + 1.3.0 8.2.2 - 4.5.25 - 2.1.0 - 3.1.471 + 4.6.0 + 2.2.1 + 3.1.561 1.2.7 1.4.0 1.5.2 @@ -130,6 +131,11 @@ yudao-spring-boot-starter-biz-error-code ${revision} + + cn.iocoder.boot + yudao-spring-boot-starter-captcha + ${revision} + @@ -452,6 +458,12 @@ ${tika-core.version} + + com.anji-plus + spring-boot-starter-captcha + ${aj-captcha.version} + + org.apache.velocity velocity-engine-core diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml index b0f5702ae..2da02638d 100644 --- a/yudao-framework/pom.xml +++ b/yudao-framework/pom.xml @@ -39,6 +39,7 @@ yudao-spring-boot-starter-biz-error-code yudao-spring-boot-starter-flowable + yudao-spring-boot-starter-captcha yudao-framework diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java new file mode 100644 index 000000000..cb9e530b7 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.framework.common.enums; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * 终端的枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum TerminalEnum implements IntArrayValuable { + + //TODO terminal 重复,请参考 '订单来源终端:[1:小程序 2:H5 3:iOS 4:安卓]' + MINI_PROGRAM(1, "小程序"), + H5(2, "H5"), + IOS(3, "iOS"), + ANDROID(3, "安卓"),; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray(); + + /** + * 终端 + */ + private final Integer terminal; + /** + * 终端名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java index 366f96008..4285b8f4c 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java @@ -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) CollectionUtil.getElementType(from.iterator())); + return ArrayUtil.toArray(from, (Class) IterUtil.getElementType(from.iterator())); } public static T get(T[] array, int index) { diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index 6b83bb3d6..d57fb21b9 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -173,6 +173,23 @@ public class CollectionUtils { return valueFunc.apply(t); } + public static > V getMinValue(List from, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return null; + } + assert from.size() > 0; // 断言,避免告警 + T t = from.stream().min(Comparator.comparing(valueFunc)).get(); + return valueFunc.apply(t); + } + + public static > V getSumValue(List from, Function valueFunc, BinaryOperator accumulator) { + if (CollUtil.isEmpty(from)) { + return null; + } + assert from.size() > 0; // 断言,避免告警 + return from.stream().map(valueFunc).reduce(accumulator).get(); + } + public static void addIfNotNull(Collection coll, T item) { if (item == null) { return; diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java index d9a01747d..6fcb29c95 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java @@ -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) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java index a9a4d24d4..5fc4e55d0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java @@ -18,7 +18,6 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.expression.operators.relational.ItemsList; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.*; @@ -37,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * 数据权限拦截器,通过 {@link DataPermissionRule} 数据权限规则,重写 SQL 的方式来实现 - * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, Table)} 方法 + * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, List)} 方法 * * 整体的代码实现上,参考 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor} 实现。 * 所以每次 MyBatis Plus 升级时,需要 Review 下其具体的实现是否有变更! @@ -53,8 +52,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme private final MappedStatementCache mappedStatementCache = new MappedStatementCache(); @Override // SELECT 场景 - public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, - RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 获得 Mapper 对应的数据权限的规则 List rules = ruleFactory.getDataPermissionRule(ms.getId()); if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过 @@ -68,12 +66,14 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme // 处理 SQL mpBs.sql(parserSingle(mpBs.sql(), null)); } finally { + // 添加是否需要重写的缓存 addMappedStatementCache(ms); + // 清空上下文 ContextHolder.clear(); } } - @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景 + @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景(因为 INSERT 不需要数据权限) public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); MappedStatement ms = mpSh.mappedStatement(); @@ -92,7 +92,9 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme // 处理 SQL mpBs.sql(parserMulti(mpBs.sql(), null)); } finally { + // 添加是否需要重写的缓存 addMappedStatementCache(ms); + // 清空上下文 ContextHolder.clear(); } } @@ -107,24 +109,6 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme } } - protected void processSelectBody(SelectBody selectBody) { - if (selectBody == null) { - return; - } - if (selectBody instanceof PlainSelect) { - processPlainSelect((PlainSelect) selectBody); - } else if (selectBody instanceof WithItem) { - WithItem withItem = (WithItem) selectBody; - processSelectBody(withItem.getSubSelect().getSelectBody()); - } else { - SetOperationList operationList = (SetOperationList) selectBody; - List selectBodys = operationList.getSelects(); - if (CollectionUtils.isNotEmpty(selectBodys)) { - selectBodys.forEach(this::processSelectBody); - } - } - } - /** * update 语句处理 */ @@ -142,28 +126,77 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme delete.setWhere(this.builderExpression(delete.getWhere(), delete.getTable())); } + // ========== 和 TenantLineInnerInterceptor 一致的逻辑 ========== + + protected void processSelectBody(SelectBody selectBody) { + if (selectBody == null) { + return; + } + if (selectBody instanceof PlainSelect) { + processPlainSelect((PlainSelect) selectBody); + } else if (selectBody instanceof WithItem) { + WithItem withItem = (WithItem) selectBody; + processSelectBody(withItem.getSubSelect().getSelectBody()); + } else { + SetOperationList operationList = (SetOperationList) selectBody; + List selectBodyList = operationList.getSelects(); + if (CollectionUtils.isNotEmpty(selectBodyList)) { + selectBodyList.forEach(this::processSelectBody); + } + } + } + /** * 处理 PlainSelect */ protected void processPlainSelect(PlainSelect plainSelect) { - FromItem fromItem = plainSelect.getFromItem(); - Expression where = plainSelect.getWhere(); - processWhereSubSelect(where); - if (fromItem instanceof Table) { - Table fromTable = (Table) fromItem; - plainSelect.setWhere(builderExpression(where, fromTable)); - } else { - processFromItem(fromItem); - } //#3087 github List selectItems = plainSelect.getSelectItems(); if (CollectionUtils.isNotEmpty(selectItems)) { selectItems.forEach(this::processSelectItem); } + + // 处理 where 中的子查询 + Expression where = plainSelect.getWhere(); + processWhereSubSelect(where); + + // 处理 fromItem + FromItem fromItem = plainSelect.getFromItem(); + List list = processFromItem(fromItem); + List
mainTables = new ArrayList<>(list); + + // 处理 join List joins = plainSelect.getJoins(); if (CollectionUtils.isNotEmpty(joins)) { - processJoins(joins); + mainTables = processJoins(mainTables, joins); } + + // 当有 mainTable 时,进行 where 条件追加 + if (CollectionUtils.isNotEmpty(mainTables)) { + plainSelect.setWhere(builderExpression(where, mainTables)); + } + } + + private List
processFromItem(FromItem fromItem) { + // 处理括号括起来的表达式 + while (fromItem instanceof ParenthesisFromItem) { + fromItem = ((ParenthesisFromItem) fromItem).getFromItem(); + } + + List
mainTables = new ArrayList<>(); + // 无 join 时的处理逻辑 + if (fromItem instanceof Table) { + Table fromTable = (Table) fromItem; + mainTables.add(fromTable); + } else if (fromItem instanceof SubJoin) { + // SubJoin 类型则还需要添加上 where 条件 + List
tables = processSubJoin((SubJoin) fromItem); + mainTables.addAll(tables); + } else { + // 处理下 fromItem + processOtherFromItem(fromItem); + } + return mainTables; } /** @@ -191,7 +224,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme return; } if (where instanceof FromItem) { - processFromItem((FromItem) where); + processOtherFromItem((FromItem) where); return; } if (where.toString().indexOf("SELECT") > 0) { @@ -204,9 +237,9 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme } else if (where instanceof InExpression) { // in InExpression expression = (InExpression) where; - ItemsList itemsList = expression.getRightItemsList(); - if (itemsList instanceof SubSelect) { - processSelectBody(((SubSelect) itemsList).getSelectBody()); + Expression inExpression = expression.getRightExpression(); + if (inExpression instanceof SubSelect) { + processSelectBody(((SubSelect) inExpression).getSelectBody()); } } else if (where instanceof ExistsExpression) { // exists @@ -239,7 +272,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme *

支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)

*

fixed gitee pulls/141

* - * @param function 函数 + * @param function */ protected void processFunction(Function function) { ExpressionList parameters = function.getParameters(); @@ -257,22 +290,19 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme /** * 处理子查询等 */ - protected void processFromItem(FromItem fromItem) { - if (fromItem instanceof SubJoin) { - SubJoin subJoin = (SubJoin) fromItem; - if (subJoin.getJoinList() != null) { - processJoins(subJoin.getJoinList()); - } - if (subJoin.getLeft() != null) { - processFromItem(subJoin.getLeft()); - } - } else if (fromItem instanceof SubSelect) { + protected void processOtherFromItem(FromItem fromItem) { + // 去除括号 + while (fromItem instanceof ParenthesisFromItem) { + fromItem = ((ParenthesisFromItem) fromItem).getFromItem(); + } + + if (fromItem instanceof SubSelect) { SubSelect subSelect = (SubSelect) fromItem; if (subSelect.getSelectBody() != null) { processSelectBody(subSelect.getSelectBody()); } } else if (fromItem instanceof ValuesList) { - logger.debug("Perform a subquery, if you do not give us feedback"); + logger.debug("Perform a subQuery, if you do not give us feedback"); } else if (fromItem instanceof LateralSubSelect) { LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem; if (lateralSubSelect.getSubSelect() != null) { @@ -284,75 +314,176 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme } } + /** + * 处理 sub join + * + * @param subJoin subJoin + * @return Table subJoin 中的主表 + */ + private List
processSubJoin(SubJoin subJoin) { + List
mainTables = new ArrayList<>(); + if (subJoin.getJoinList() != null) { + List
list = processFromItem(subJoin.getLeft()); + mainTables.addAll(list); + mainTables = processJoins(mainTables, subJoin.getJoinList()); + } + return mainTables; + } + /** * 处理 joins * - * @param joins join 集合 + * @param mainTables 可以为 null + * @param joins join 集合 + * @return List
右连接查询的 Table 列表 */ - private void processJoins(List joins) { + private List
processJoins(List
mainTables, List joins) { + // join 表达式中最终的主表 + Table mainTable = null; + // 当前 join 的左表 + Table leftTable = null; + + if (mainTables == null) { + mainTables = new ArrayList<>(); + } else if (mainTables.size() == 1) { + mainTable = mainTables.get(0); + leftTable = mainTable; + } + //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名 - Deque
tables = new LinkedList<>(); + Deque> onTableDeque = new LinkedList<>(); for (Join join : joins) { // 处理 on 表达式 - FromItem fromItem = join.getRightItem(); - if (fromItem instanceof Table) { - Table fromTable = (Table) fromItem; + FromItem joinItem = join.getRightItem(); + + // 获取当前 join 的表,subJoint 可以看作是一张表 + List
joinTables = null; + if (joinItem instanceof Table) { + joinTables = new ArrayList<>(); + joinTables.add((Table) joinItem); + } else if (joinItem instanceof SubJoin) { + joinTables = processSubJoin((SubJoin) joinItem); + } + + if (joinTables != null) { + + // 如果是隐式内连接 + if (join.isSimple()) { + mainTables.addAll(joinTables); + continue; + } + + // 当前表是否忽略 + Table joinTable = joinTables.get(0); + + List
onTables = null; + // 如果不要忽略,且是右连接,则记录下当前表 + if (join.isRight()) { + mainTable = joinTable; + if (leftTable != null) { + onTables = Collections.singletonList(leftTable); + } + } else if (join.isLeft()) { + onTables = Collections.singletonList(joinTable); + } else if (join.isInner()) { + if (mainTable == null) { + onTables = Collections.singletonList(joinTable); + } else { + onTables = Arrays.asList(mainTable, joinTable); + } + mainTable = null; + } + + mainTables = new ArrayList<>(); + if (mainTable != null) { + mainTables.add(mainTable); + } + // 获取 join 尾缀的 on 表达式列表 Collection originOnExpressions = join.getOnExpressions(); // 正常 join on 表达式只有一个,立刻处理 - if (originOnExpressions.size() == 1) { - processJoin(join); + if (originOnExpressions.size() == 1 && onTables != null) { + List onExpressions = new LinkedList<>(); + onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables)); + join.setOnExpressions(onExpressions); + leftTable = joinTable; continue; } - tables.push(fromTable); + // 表名压栈,忽略的表压入 null,以便后续不处理 + onTableDeque.push(onTables); // 尾缀多个 on 表达式的时候统一处理 if (originOnExpressions.size() > 1) { Collection onExpressions = new LinkedList<>(); for (Expression originOnExpression : originOnExpressions) { - Table currentTable = tables.poll(); - onExpressions.add(builderExpression(originOnExpression, currentTable)); + List
currentTableList = onTableDeque.poll(); + if (CollectionUtils.isEmpty(currentTableList)) { + onExpressions.add(originOnExpression); + } else { + onExpressions.add(builderExpression(originOnExpression, currentTableList)); + } } join.setOnExpressions(onExpressions); } + leftTable = joinTable; } else { - // 处理右边连接的子表达式 - processFromItem(fromItem); + processOtherFromItem(joinItem); + leftTable = null; } } + + return mainTables; } + // ========== 和 TenantLineInnerInterceptor 存在差异的逻辑:关键,实现权限条件的拼接 ========== + /** - * 处理联接语句 + * 处理条件 + * + * @param currentExpression 当前 where 条件 + * @param table 单个表 */ - protected void processJoin(Join join) { - if (join.getRightItem() instanceof Table) { - Table fromTable = (Table) join.getRightItem(); - Expression originOnExpression = CollUtil.getFirst(join.getOnExpressions()); - originOnExpression = builderExpression(originOnExpression, fromTable); - join.setOnExpressions(CollUtil.newArrayList(originOnExpression)); - } + protected Expression builderExpression(Expression currentExpression, Table table) { + return this.builderExpression(currentExpression, Collections.singletonList(table)); } /** * 处理条件 + * + * @param currentExpression 当前 where 条件 + * @param tables 多个表 */ - protected Expression builderExpression(Expression currentExpression, Table table) { - // 获得 Table 对应的数据权限条件 - Expression equalsTo = buildDataPermissionExpression(table); - if (equalsTo == null) { // 如果没条件,则返回 currentExpression 默认 + protected Expression builderExpression(Expression currentExpression, List
tables) { + // 没有表需要处理直接返回 + if (CollectionUtils.isEmpty(tables)) { return currentExpression; } - // 表达式为空,则直接返回 equalsTo + // 第一步,获得 Table 对应的数据权限条件 + Expression dataPermissionExpression = null; + for (Table table : tables) { + // 构建每个表的权限 Expression 条件 + Expression expression = buildDataPermissionExpression(table); + if (expression == null) { + continue; + } + // 合并到 dataPermissionExpression 中 + dataPermissionExpression = dataPermissionExpression == null ? expression + : new AndExpression(dataPermissionExpression, expression); + } + + // 第二步,合并多个 Expression 条件 + if (dataPermissionExpression == null) { + return currentExpression; + } if (currentExpression == null) { - return equalsTo; + return dataPermissionExpression; } - // 如果表达式为 Or,则需要 (currentExpression) AND equalsTo + // ① 如果表达式为 Or,则需要 (currentExpression) AND dataPermissionExpression if (currentExpression instanceof OrExpression) { - return new AndExpression(new Parenthesis(currentExpression), equalsTo); + return new AndExpression(new Parenthesis(currentExpression), dataPermissionExpression); } - // 如果表达式为 And,则直接返回 currentExpression AND equalsTo - return new AndExpression(currentExpression, equalsTo); + // ② 如果表达式为 And,则直接返回 where AND dataPermissionExpression + return new AndExpression(currentExpression, dataPermissionExpression); } /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java index 8c0772f1a..b8cad13cf 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java @@ -46,7 +46,7 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest @Override public Set getTableNames() { - return asSet("entity", "entity1", "entity2", "t1", "t2", // 支持 MyBatis Plus 的单元测试 + return asSet("entity", "entity1", "entity2", "entity3", "t1", "t2", "sys_dict_item", // 支持 MyBatis Plus 的单元测试 "t_user", "t_role"); // 满足自己的单元测试 } @@ -84,30 +84,30 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest @Test void delete() { assertSql("delete from entity where id = ?", - "DELETE FROM entity WHERE id = ? AND tenant_id = 1"); + "DELETE FROM entity WHERE id = ? AND entity.tenant_id = 1"); } @Test void update() { assertSql("update entity set name = ? where id = ?", - "UPDATE entity SET name = ? WHERE id = ? AND tenant_id = 1"); + "UPDATE entity SET name = ? WHERE id = ? AND entity.tenant_id = 1"); } @Test void selectSingle() { // 单表 assertSql("select * from entity where id = ?", - "SELECT * FROM entity WHERE id = ? AND tenant_id = 1"); + "SELECT * FROM entity WHERE id = ? AND entity.tenant_id = 1"); assertSql("select * from entity where id = ? or name = ?", - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1"); + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1"); assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)", - "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1"); + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND entity.tenant_id = 1"); /* not */ assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)", - "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1"); + "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND entity.tenant_id = 1"); } @Test @@ -167,10 +167,12 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest assertSql("SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)", "SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); + /* <= */ assertSql("SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)", "SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); + /* <> */ assertSql("SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)", "SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1"); @@ -204,6 +206,14 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest "SELECT * FROM entity e " + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); + + assertSql("SELECT * FROM entity e " + + "left join entity1 e1 on e1.id = e.id " + + "left join entity2 e2 on e1.id = e2.id", + "SELECT * FROM entity e " + + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " + + "WHERE e.tenant_id = 1"); } @Test @@ -212,17 +222,125 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest assertSql("SELECT * FROM entity e " + "right join entity1 e1 on e1.id = e.id", "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE e.tenant_id = 1"); + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + + "WHERE e1.tenant_id = 1"); + + assertSql("SELECT * FROM with_as_1 e " + + "right join entity1 e1 on e1.id = e.id", + "SELECT * FROM with_as_1 e " + + "RIGHT JOIN entity1 e1 ON e1.id = e.id " + + "WHERE e1.tenant_id = 1"); assertSql("SELECT * FROM entity e " + "right join entity1 e1 on e1.id = e.id " + "WHERE e.id = ? OR e.name = ?", "SELECT * FROM entity e " + - "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); + + assertSql("SELECT * FROM entity e " + + "right join entity1 e1 on e1.id = e.id " + + "right join entity2 e2 on e1.id = e2.id ", + "SELECT * FROM entity e " + + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + + "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " + + "WHERE e2.tenant_id = 1"); } + @Test + void selectMixJoin() { + assertSql("SELECT * FROM entity e " + + "right join entity1 e1 on e1.id = e.id " + + "left join entity2 e2 on e1.id = e2.id", + "SELECT * FROM entity e " + + "RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 " + + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 " + + "WHERE e1.tenant_id = 1"); + + assertSql("SELECT * FROM entity e " + + "left join entity1 e1 on e1.id = e.id " + + "right join entity2 e2 on e1.id = e2.id", + "SELECT * FROM entity e " + + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + + "RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 " + + "WHERE e2.tenant_id = 1"); + + assertSql("SELECT * FROM entity e " + + "left join entity1 e1 on e1.id = e.id " + + "inner join entity2 e2 on e1.id = e2.id", + "SELECT * FROM entity e " + + "LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + + "INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1"); + } + + + @Test + void selectJoinSubSelect() { + assertSql("select * from (select * from entity) e1 " + + "left join entity2 e2 on e1.id = e2.id", + "SELECT * FROM (SELECT * FROM entity WHERE entity.tenant_id = 1) e1 " + + "LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1"); + + assertSql("select * from entity1 e1 " + + "left join (select * from entity2) e2 " + + "on e1.id = e2.id", + "SELECT * FROM entity1 e1 " + + "LEFT JOIN (SELECT * FROM entity2 WHERE entity2.tenant_id = 1) e2 " + + "ON e1.id = e2.id " + + "WHERE e1.tenant_id = 1"); + } + + @Test + void selectSubJoin() { + + assertSql("select * FROM " + + "(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)", + "SELECT * FROM " + + "(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " + + "WHERE e2.tenant_id = 1"); + + assertSql("select * FROM " + + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)", + "SELECT * FROM " + + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + + "WHERE e1.tenant_id = 1"); + + + assertSql("select * FROM " + + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) " + + "right join entity3 e3 on e1.id = e3.id", + "SELECT * FROM " + + "(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + + "RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 " + + "WHERE e3.tenant_id = 1"); + + + assertSql("select * FROM entity e " + + "LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) " + + "on e.id = e2.id", + "SELECT * FROM entity e " + + "LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) " + + "ON e.id = e2.id AND e2.tenant_id = 1 " + + "WHERE e.tenant_id = 1"); + + assertSql("select * FROM entity e " + + "LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " + + "on e.id = e2.id", + "SELECT * FROM entity e " + + "LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + + "ON e.id = e2.id AND e1.tenant_id = 1 " + + "WHERE e.tenant_id = 1"); + + assertSql("select * FROM entity e " + + "RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) " + + "on e.id = e2.id", + "SELECT * FROM entity e " + + "RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) " + + "ON e.id = e2.id AND e.tenant_id = 1 " + + "WHERE e1.tenant_id = 1"); + } + + @Test void selectLeftJoinMultipleTrailingOn() { // 多个 on 尾缀的 @@ -256,51 +374,97 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest "inner join entity1 e1 on e1.id = e.id " + "WHERE e.id = ? OR e.name = ?", "SELECT * FROM entity e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " + + "WHERE e.id = ? OR e.name = ?"); assertSql("SELECT * FROM entity e " + "inner join entity1 e1 on e1.id = e.id " + "WHERE (e.id = ? OR e.name = ?)", "SELECT * FROM entity e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 " + + "WHERE (e.id = ? OR e.name = ?)"); + + // 隐式内连接 + assertSql("SELECT * FROM entity,entity1 " + + "WHERE entity.id = entity1.id", + "SELECT * FROM entity, entity1 " + + "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); + + // 隐式内连接 + assertSql("SELECT * FROM entity a, with_as_entity1 b " + + "WHERE a.id = b.id", + "SELECT * FROM entity a, with_as_entity1 b " + + "WHERE a.id = b.id AND a.tenant_id = 1"); + + assertSql("SELECT * FROM with_as_entity a, with_as_entity1 b " + + "WHERE a.id = b.id", + "SELECT * FROM with_as_entity a, with_as_entity1 b " + + "WHERE a.id = b.id"); + + // SubJoin with 隐式内连接 + assertSql("SELECT * FROM (entity,entity1) " + + "WHERE entity.id = entity1.id", + "SELECT * FROM (entity, entity1) " + + "WHERE entity.id = entity1.id " + + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); + + assertSql("SELECT * FROM ((entity,entity1),entity2) " + + "WHERE entity.id = entity1.id and entity.id = entity2.id", + "SELECT * FROM ((entity, entity1), entity2) " + + "WHERE entity.id = entity1.id AND entity.id = entity2.id " + + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1"); + + assertSql("SELECT * FROM (entity,(entity1,entity2)) " + + "WHERE entity.id = entity1.id and entity.id = entity2.id", + "SELECT * FROM (entity, (entity1, entity2)) " + + "WHERE entity.id = entity1.id AND entity.id = entity2.id " + + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1 AND entity2.tenant_id = 1"); + + // 沙雕的括号写法 + assertSql("SELECT * FROM (((entity,entity1))) " + + "WHERE entity.id = entity1.id", + "SELECT * FROM (((entity, entity1))) " + + "WHERE entity.id = entity1.id " + + "AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); - // 垃圾 inner join todo -// assertSql("SELECT * FROM entity,entity1 " + -// "WHERE entity.id = entity1.id", -// "SELECT * FROM entity e " + -// "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + -// "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); } + @Test void selectWithAs() { assertSql("with with_as_A as (select * from entity) select * from with_as_A", - "WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A"); + "WITH with_as_A AS (SELECT * FROM entity WHERE entity.tenant_id = 1) SELECT * FROM with_as_A"); + } + + + @Test + void selectIgnoreTable() { + assertSql(" SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)", + "SELECT dict.dict_code, item.item_text AS \"text\", item.item_value AS \"value\" FROM sys_dict_item item INNER JOIN sys_dict dict ON dict.id = item.dict_id AND item.tenant_id = 1 WHERE dict.dict_code IN (1, 2, 3) AND item.item_value IN (1, 2, 3)"); } private void assertSql(String sql, String targetSql) { assertEquals(targetSql, interceptor.parserSingle(sql, null)); } + // ========== 额外的测试 ========== @Test public void testSelectSingle() { // 单表 assertSql("select * from t_user where id = ?", - "SELECT * FROM t_user WHERE id = ? AND tenant_id = 1 AND dept_id IN (10, 20)"); + "SELECT * FROM t_user WHERE id = ? AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); assertSql("select * from t_user where id = ? or name = ?", - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)"); + "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); assertSql("SELECT * FROM t_user WHERE (id = ? OR name = ?)", - "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)"); + "SELECT * FROM t_user WHERE (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); /* not */ assertSql("SELECT * FROM t_user WHERE not (id = ? OR name = ?)", - "SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND tenant_id = 1 AND dept_id IN (10, 20)"); + "SELECT * FROM t_user WHERE NOT (id = ? OR name = ?) AND t_user.tenant_id = 1 AND t_user.dept_id IN (10, 20)"); } @Test @@ -329,16 +493,16 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest "right join t_role e1 on e1.id = e.id " + "WHERE e.id = ? OR e.name = ?", "SELECT * FROM t_user e " + - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); + "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " + + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); // 条件 e.id = ? OR e.name = ? 带括号 assertSql("SELECT * FROM t_user e " + "right join t_role e1 on e1.id = e.id " + "WHERE (e.id = ? OR e.name = ?)", "SELECT * FROM t_user e " + - "RIGHT JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); + "RIGHT JOIN t_role e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) " + + "WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1"); } @Test @@ -348,23 +512,22 @@ public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest "inner join entity1 e1 on e1.id = e.id " + "WHERE e.id = ? OR e.name = ?", "SELECT * FROM t_user e " + - "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " + + "WHERE e.id = ? OR e.name = ?"); // 条件 e.id = ? OR e.name = ? 带括号 assertSql("SELECT * FROM t_user e " + - "inner join t_role e1 on e1.id = e.id " + + "inner join entity1 e1 on e1.id = e.id " + "WHERE (e.id = ? OR e.name = ?)", "SELECT * FROM t_user e " + - "INNER JOIN t_role e1 ON e1.id = e.id AND e1.tenant_id = 1 " + - "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1 AND e.dept_id IN (10, 20)"); + "INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e.dept_id IN (10, 20) AND e1.tenant_id = 1 " + + "WHERE (e.id = ? OR e.name = ?)"); - // 垃圾 inner join todo -// assertSql("SELECT * FROM entity,entity1 " + -// "WHERE entity.id = entity1.id", -// "SELECT * FROM entity e " + -// "INNER JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 " + -// "WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1"); + // 没有 On 的 inner join + assertSql("SELECT * FROM entity,entity1 " + + "WHERE entity.id = entity1.id", + "SELECT * FROM entity, entity1 " + + "WHERE entity.id = entity1.id AND entity.tenant_id = 1 AND entity1.tenant_id = 1"); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java index 866dcb46d..58e1258b0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java @@ -27,7 +27,7 @@ public class DictFrameworkUtils { /** * 针对 {@link #getDictDataLabel(String, String)} 的缓存 */ - private static final LoadingCache, DictDataRespDTO> getDictDataCache = CacheUtils.buildAsyncReloadingCache( + private static final LoadingCache, DictDataRespDTO> GET_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( Duration.ofMinutes(1L), // 过期时间 1 分钟 new CacheLoader, DictDataRespDTO>() { @@ -41,7 +41,7 @@ public class DictFrameworkUtils { /** * 针对 {@link #parseDictDataValue(String, String)} 的缓存 */ - private static final LoadingCache, DictDataRespDTO> parseDictDataCache = CacheUtils.buildAsyncReloadingCache( + private static final LoadingCache, DictDataRespDTO> PARSE_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( Duration.ofMinutes(1L), // 过期时间 1 分钟 new CacheLoader, 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(); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml index 4153c25c4..e6106b9d9 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml @@ -52,12 +52,18 @@ com.alipay.sdk alipay-sdk-java - 4.17.9.ALL + 4.31.72.ALL + + + org.bouncycastle + bcprov-jdk15on + + com.github.binarywang weixin-java-pay - 4.1.9.B + 4.3.8.B diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index 292b6cf01..3253709c8 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java @@ -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 implemen @Override public final PayCommonResult unifiedOrder(PayOrderUnifiedReqDTO reqDTO) { - ValidationUtil.validate(reqDTO); + Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); // 执行短信发送 PayCommonResult result; try { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java index a4c14f634..9ded6f026 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn.iocoder.yudao.framework.pay.core.client.impl/alipay/AlipayQrPayClientTest.java @@ -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; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml index 9572391db..2dc2fe688 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml @@ -35,12 +35,12 @@ com.github.binarywang wx-java-mp-spring-boot-starter - 4.3.4.B + 4.3.8.B com.github.binarywang wx-java-miniapp-spring-boot-starter - 4.3.4.B + 4.3.8.B diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/pom.xml b/yudao-framework/yudao-spring-boot-starter-captcha/pom.xml new file mode 100644 index 000000000..c2d237406 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/pom.xml @@ -0,0 +1,39 @@ + + + + cn.iocoder.boot + yudao-framework + ${revision} + + 4.0.0 + yudao-spring-boot-starter-captcha + jar + + ${project.artifactId} + 验证码拓展 + 1. 基于 aj-captcha 实现滑块验证码,文档:https://ajcaptcha.beliefteam.cn/captcha-doc/ + + + + + + org.springframework.boot + spring-boot-starter + + + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + com.anji-plus + spring-boot-starter-captcha + + + + diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java new file mode 100644 index 000000000..0f47b0844 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/config/YudaoCaptchaConfiguration.java @@ -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); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java new file mode 100644 index 000000000..5d17e0477 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/enums/CaptchaRedisKeyConstants.java @@ -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} + +} diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/service/RedisCaptchaServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/service/RedisCaptchaServiceImpl.java new file mode 100644 index 000000000..c14901efb --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/core/service/RedisCaptchaServiceImpl.java @@ -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); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/package-info.java b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/package-info.java new file mode 100644 index 000000000..e78d9eab2 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/java/cn/iocoder/yudao/framework/captcha/package-info.java @@ -0,0 +1,7 @@ +/** + * 验证码拓展 + * 1. 基于 aj-captcha 实现滑块验证码,文档:https://ajcaptcha.beliefteam.cn/captcha-doc/ + * + * @author 星语 + */ +package cn.iocoder.yudao.framework.captcha; diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService new file mode 100644 index 000000000..afede97de --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService @@ -0,0 +1 @@ +cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..ed8b528ff --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + cn.iocoder.yudao.framework.captcha.config.YudaoCaptchaConfiguration diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg1.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg1.png new file mode 100644 index 000000000..c48145769 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg1.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg2.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg2.png new file mode 100644 index 000000000..bf8fb38ff Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg2.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg3.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg3.png new file mode 100644 index 000000000..f871d3d12 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg3.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg4.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg4.png new file mode 100644 index 000000000..2e3d87166 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg4.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg5.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg5.png new file mode 100644 index 000000000..fe383b720 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg5.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg6.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg6.png new file mode 100644 index 000000000..5024ceb22 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg6.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg7.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg7.png new file mode 100644 index 000000000..efe76f8de Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg7.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg8.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg8.png new file mode 100644 index 000000000..2727aa324 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg8.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg9.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg9.png new file mode 100644 index 000000000..4463aa2fb Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/original/bg9.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/1.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/1.png new file mode 100644 index 000000000..ef1132471 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/1.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/10.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/10.png new file mode 100644 index 000000000..297e44cf4 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/10.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/11.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/11.png new file mode 100644 index 000000000..d9b1da8d7 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/11.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/12.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/12.png new file mode 100644 index 000000000..07e7313b4 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/12.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/13.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/13.png new file mode 100644 index 000000000..82c3dd969 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/13.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/14.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/14.png new file mode 100644 index 000000000..0b9a86615 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/14.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/15.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/15.png new file mode 100644 index 000000000..86b0d1cf1 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/15.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/16.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/16.png new file mode 100644 index 000000000..e90a6e292 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/16.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/17.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/17.png new file mode 100644 index 000000000..a82cbc7c4 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/17.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/18.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/18.png new file mode 100644 index 000000000..d3f3cfd03 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/18.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/19.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/19.png new file mode 100644 index 000000000..eb2855bd8 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/19.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/8.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/8.png new file mode 100644 index 000000000..3cb5ce1c8 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/8.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/9.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/9.png new file mode 100644 index 000000000..384d35415 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/11/9.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/2.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/2.png new file mode 100644 index 000000000..baf3f06d7 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/2.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/3.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/3.png new file mode 100644 index 000000000..ccaf61723 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/3.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/4.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/4.png new file mode 100644 index 000000000..7dab16223 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/jigsaw/slidingBlock/4.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg1.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg1.png new file mode 100644 index 000000000..14e73454a Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg1.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg10.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg10.png new file mode 100644 index 000000000..1ea1d6d59 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg10.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg2.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg2.png new file mode 100644 index 000000000..0edb32937 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg2.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg3.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg3.png new file mode 100644 index 000000000..91679960f Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg3.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg4.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg4.png new file mode 100644 index 000000000..e8e8e6c0c Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg4.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg5.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg5.png new file mode 100644 index 000000000..66a3181e7 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg5.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg6.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg6.png new file mode 100644 index 000000000..9b0f5d8c1 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg6.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg7.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg7.png new file mode 100644 index 000000000..db41c74a0 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg7.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg8.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg8.png new file mode 100644 index 000000000..349681306 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg8.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg9.png b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg9.png new file mode 100644 index 000000000..4e7b47752 Binary files /dev/null and b/yudao-framework/yudao-spring-boot-starter-captcha/src/main/resources/images/pic-click/bg9.png differ diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/JsonLongSetTypeHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/JsonLongSetTypeHandler.java index ed6d81baa..052c7232e 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/JsonLongSetTypeHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/JsonLongSetTypeHandler.java @@ -16,11 +16,11 @@ import java.util.Set; */ public class JsonLongSetTypeHandler extends AbstractJsonTypeHandler { - private static final TypeReference> typeReference = new TypeReference>(){}; + private static final TypeReference> TYPE_REFERENCE = new TypeReference>(){}; @Override protected Object parse(String json) { - return JsonUtils.parseObject(json, typeReference); + return JsonUtils.parseObject(json, TYPE_REFERENCE); } @Override diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java index 16d3b29b1..0b1b01b08 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.SortingField; import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -78,7 +79,10 @@ public class MyBatisUtils { * @return Column 对象 */ public static Column buildColumn(String tableName, Alias tableAlias, String column) { - return new Column(tableAlias != null ? tableAlias.getName() + "." + column : column); + if (tableAlias != null) { + tableName = tableAlias.getName(); + } + return new Column(tableName + StringPool.DOT + column); } } diff --git a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java index 882d8d3b1..4ef3b92f3 100644 --- a/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java +++ b/yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/core/RedisKeyRegistry.java @@ -11,18 +11,18 @@ public class RedisKeyRegistry { /** * Redis RedisKeyDefine 数组 */ - private static final List defines = new ArrayList<>(); + private static final List DEFINES = new ArrayList<>(); public static void add(RedisKeyDefine define) { - defines.add(define); + DEFINES.add(define); } public static List list() { - return defines; + return DEFINES; } public static int size() { - return defines.size(); + return DEFINES.size(); } } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java index c9a789f41..1e7518787 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java @@ -81,7 +81,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap /** * 配置 URL 的安全配置 - * + *

* 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 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 diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java index 5e46daa1e..1bdbe712f 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/context/TransmittableThreadLocalSecurityContextHolderStrategy.java @@ -17,19 +17,19 @@ public class TransmittableThreadLocalSecurityContextHolderStrategy implements Se /** * 使用 TransmittableThreadLocal 作为上下文 */ - private static final ThreadLocal contextHolder = new TransmittableThreadLocal<>(); + private static final ThreadLocal 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 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java index 89dfd1227..032370158 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java @@ -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)); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java index 7523fab6d..90f5816f3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java @@ -31,7 +31,7 @@ public class CodegenBuilder { * 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射 * 注意,字段的匹配以后缀的方式 */ - private static final Map columnListOperationConditionMappings = + private static final Map COLUMN_LIST_OPERATION_CONDITION_MAPPINGS = MapUtil.builder() .put("name", CodegenColumnListConditionEnum.LIKE) .put("time", CodegenColumnListConditionEnum.BETWEEN) @@ -42,7 +42,7 @@ public class CodegenBuilder { * 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射 * 注意,字段的匹配以后缀的方式 */ - private static final Map columnHtmlTypeMappings = + private static final Map COLUMN_HTML_TYPE_MAPPINGS = MapUtil.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 类型. diff --git a/yudao-module-mall/pom.xml b/yudao-module-mall/pom.xml index 866874852..68c1825cc 100644 --- a/yudao-module-mall/pom.xml +++ b/yudao-module-mall/pom.xml @@ -15,14 +15,18 @@ ${project.artifactId} - market模块,主要实现营销相关功能 - 例如:营销活动、banner广告、优惠券、优惠码等功能。 + 商城大模块,由 product 商品、market 营销、trade 交易 coupon等组成 + yudao-module-coupon-api + yudao-module-coupon-biz yudao-module-market-api yudao-module-market-biz yudao-module-product-api yudao-module-product-biz + yudao-module-trade-api + yudao-module-trade-biz + diff --git a/yudao-module-mall/yudao-module-coupon-api/pom.xml b/yudao-module-mall/yudao-module-coupon-api/pom.xml new file mode 100644 index 000000000..6dab80613 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/pom.xml @@ -0,0 +1,35 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + + 4.0.0 + yudao-module-coupon-api + jar + + + ${project.artifactId} + + coupon 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java new file mode 100644 index 000000000..0e1e17ece --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponExpireTimeTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 是否开启过期提醒 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponExpireTimeTypeEnum { + + OPEN(1,"不开启"), + CLOSE(0,"开启"),; + + /** + * 是否开启过期提醒 + */ + private final Integer type; + /** + * 是否开启过期提醒 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java new file mode 100644 index 000000000..66a86bc84 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponFetchTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 领取是否无限制 0 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponFetchTypeEnum { + + LIMIT(1,"限制"), + NOT_LIMIT(0,"不限制"),; + + /** + * 是否开启过期提醒 + */ + private final Integer type; + /** + * 是否开启过期提醒 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java new file mode 100644 index 000000000..1ac9e9f68 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponForbidPreferenceEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 优惠叠加类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponForbidPreferenceEnum { + + UN_FORBID(0,"不限制"), + FORBID(1,"优惠券仅原价购买商品时可用"); + + /** + * 优惠券类型 + */ + private final Integer type; + /** + * 优惠券类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java new file mode 100644 index 000000000..e0946b7a0 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponGoodsTypeEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 优惠券商品使用类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponGoodsTypeEnum { + + ALL(1,"全部商品可用"), + POINT_PRODUCT(2,"指定商品可用"), + POINT_PRODUCT_NOT(3,"指定商品不可用"),; + + /** + * 优惠券商品使用类型 + */ + private final Integer type; + /** + * 优惠券商品使用类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java new file mode 100644 index 000000000..813084731 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponStatusTypeEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 优惠券状态类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponStatusTypeEnum { + + PROCESSING(1,"进行中"), + END(2,"已结束"), + CLOSE(3,"已关闭"),; + + /** + * 优惠券类型 + */ + private final Integer type; + /** + * 优惠券类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java new file mode 100644 index 000000000..639d5080d --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponTypeEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券 - 优惠券类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponTypeEnum { + + REWARD(1,"满减"), + DISCOUNT(2,"折扣"), + RANDOW(3,"随机"),; + + /** + * 优惠券类型 + */ + private final Integer type; + /** + * 优惠券类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java new file mode 100644 index 000000000..beb530f0c --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponUseLimitEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +/** + * 优惠券使用类型 - 优惠券使用类型类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponUseLimitEnum { + + HAS_LIMIT(1,"无门槛"), + NO_LIMIT(2,"有门槛"),; + + /** + * 优惠券使用类型 + */ + private final Integer type; + /** + * 优惠券使用类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java new file mode 100644 index 000000000..798cf1e0f --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/CouponValidityTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 过期类型 - 状态 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum CouponValidityTypeEnum { + + TIME_RANGE_EXPIRTED(1,"时间范围过期"), + EXPIRES_AFTER_FIXED_DATE(2,"领取之日固定日期后过期"), + EXPIRES_DATE_NEXT_FIEXD_DATE(3,"领取次日固定日期后过期"),; + + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..fced9d140 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-api/src/main/java/cn/iocoder/yudao/module/CouponTemplete/enums/ErrorCodeConstants.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.CouponTemplete.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * coupon 优惠券错误码枚举类 + * + * coupon 优惠券系统,使用 1-010-000-000 段 + */ +public interface ErrorCodeConstants { + // ========== COUPON分类相关 1010001000 ============ + + ErrorCode COUPON_TEMPLETE_NOT_EXISTS = new ErrorCode(1010001000, "优惠券模板不存在"); + ErrorCode MONEY_NOT_NULL = new ErrorCode(1010001001, "当type为reward时需要添加发放面额不能为空"); + ErrorCode DISCOUNT_NOT_NULL = new ErrorCode(1010001001, "当type为discount时需要添加折扣不能为空"); + ErrorCode DISCOUNT_LIMIT_NOT_NULL = new ErrorCode(1010001001, "当type为discount时可选择性添加最多折扣金额不能为空"); + ErrorCode MIN_MAX_NOT_NULL = new ErrorCode(1010001001, "当type为radom时需要添加最低金额"); + ErrorCode START_END_TIME_NOT_NULL = new ErrorCode(1010001001, "使用开始日期,使用结束日期不能为空"); + ErrorCode FIXED_TERM_NOT_NULL = new ErrorCode(1010001001, "领取之日起或者次日N天内有效不能为空"); + + + // ========== COUPON分类相关 1010002000 ============ + ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1010001000, "优惠券模板不存在"); + + +} + diff --git a/yudao-module-mall/yudao-module-coupon-biz/pom.xml b/yudao-module-mall/yudao-module-coupon-biz/pom.xml new file mode 100644 index 000000000..eda16bd16 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/pom.xml @@ -0,0 +1,62 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + 4.0.0 + jar + yudao-module-coupon-biz + + ${project.artifactId} + + + coupon模块,主要负责优惠券的一些业务,含发布优惠券模板,分发优惠券等 + + + + + + cn.iocoder.boot + yudao-module-coupon-api + ${revision} + + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-operatelog + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + yudao-spring-boot-starter-web + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + + + + + \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java new file mode 100644 index 000000000..8ee02c366 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/CouponController.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.annotations.*; + +import javax.validation.*; +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; +import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.module.coupon.convert.coupon.CouponConvert; +import cn.iocoder.yudao.module.coupon.service.coupon.CouponService; + +@Api(tags = "管理后台 - 优惠券") +@RestController +@RequestMapping("/coupon/item") +@Validated +public class CouponController { + + @Resource + private CouponService couponService; + + + //todo 用户优惠券 + @PostMapping("/create") + @ApiOperation("用户领取优惠券") + @PreAuthorize("@ss.hasPermission('coupon::create')") + public CommonResult create(@RequestParam("couponTemplateId") Long couponTemplateId) { + + return success(couponService.create(couponTemplateId)); + } + + + @PutMapping("/update") + @ApiOperation("更新优惠券") + @PreAuthorize("@ss.hasPermission('coupon::update')") + public CommonResult update(@Valid @RequestBody CouponUpdateReqVO updateReqVO) { + couponService.update(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除优惠券") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('coupon::delete')") + public CommonResult delete(@RequestParam("id") Long id) { + couponService.delete(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得优惠券") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('coupon::query')") + public CommonResult get(@RequestParam("id") Long id) { + CouponDO couponDO = couponService.get(id); + return success(CouponConvert.INSTANCE.convert(couponDO)); + } + + + + @GetMapping("/list") + @ApiOperation("获得优惠券列表") + @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) + @PreAuthorize("@ss.hasPermission('coupon::query')") + public CommonResult> getList(@RequestParam("ids") Collection ids) { + List list = couponService.getList(ids); + return success(CouponConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @ApiOperation("获得优惠券分页") + @PreAuthorize("@ss.hasPermission('coupon::query')") + public CommonResult> getPage(@Valid CouponPageReqVO pageVO) { + PageResult pageResult = couponService.getPage(pageVO); + return success(CouponConvert.INSTANCE.convertPage(pageResult)); + } + + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java new file mode 100644 index 000000000..31cc828f7 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponBaseVO.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import io.swagger.annotations.*; +import javax.validation.constraints.*; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +/** +* 优惠券 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class CouponBaseVO { + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机", required = true) + @NotNull(message = "优惠券类型 reward-满减 discount-折扣 random-随机不能为空") + private String type; + + @ApiModelProperty(value = "优惠券名称", required = true) + @NotNull(message = "优惠券名称不能为空") + private String name; + + @ApiModelProperty(value = "优惠券类型id") + private Long couponTypeId; + + @ApiModelProperty(value = "优惠券编码", required = true) + @NotNull(message = "优惠券编码不能为空") + private String couponCode; + + @ApiModelProperty(value = "领用人", required = true) + @NotNull(message = "领用人不能为空") + private Long memberId; + + @ApiModelProperty(value = "优惠券使用订单id", required = true) + @NotNull(message = "优惠券使用订单id不能为空") + private Long useOrderId; + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用", required = true) + @NotNull(message = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用不能为空") + private Boolean goodsType; + + @ApiModelProperty(value = "适用商品id", required = true) + @NotNull(message = "适用商品id不能为空") + private String goodsIds; + + @ApiModelProperty(value = "最小金额", required = true) + @NotNull(message = "最小金额不能为空") + private BigDecimal atLeast; + + @ApiModelProperty(value = "面额", required = true) + @NotNull(message = "面额不能为空") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加", required = true) + @NotNull(message = "1 =< 折扣 <= 9.9 当type为discount时需要添加不能为空") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加", required = true) + @NotNull(message = "最多折扣金额 当type为discount时可选择性添加不能为空") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用", required = true) + @NotNull(message = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用不能为空") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启", required = true) + @NotNull(message = "是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒", required = true) + @NotNull(message = "过期前N天提醒不能为空") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "是否已提醒", required = true) + @NotNull(message = "是否已提醒不能为空") + private Boolean whetherNoticed; + + @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期", required = true) + @NotNull(message = "优惠券状态 1已领用(未使用) 2已使用 3已过期不能为空") + private Integer state; + + @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取", required = true) + @NotNull(message = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取不能为空") + private Boolean getType; + + @ApiModelProperty(value = "领取时间", required = true) + @NotNull(message = "领取时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date fetchTime; + + @ApiModelProperty(value = "使用时间", required = true) + @NotNull(message = "使用时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date useTime; + + @ApiModelProperty(value = "可使用的开始时间", required = true) + @NotNull(message = "可使用的开始时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date startTime; + + @ApiModelProperty(value = "有效期结束时间", required = true) + @NotNull(message = "有效期结束时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date endTime; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java old mode 100755 new mode 100644 similarity index 50% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateReqVO.java rename to yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java index e01c272a4..2cd3cb419 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateReqVO.java +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponCreateReqVO.java @@ -1,14 +1,14 @@ -package cn.iocoder.yudao.module.product.controller.admin.sku.vo; +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; import lombok.*; import java.util.*; import io.swagger.annotations.*; import javax.validation.constraints.*; -@ApiModel("管理后台 - 商品sku创建 Request VO") +@ApiModel("管理后台 - 优惠券创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class ProductSkuCreateReqVO extends ProductSkuBaseVO { +public class CouponCreateReqVO extends CouponBaseVO { } diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java new file mode 100644 index 000000000..f7d1d1d6f --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExcelVO.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import io.swagger.annotations.*; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * 优惠券 Excel VO + * + * @author wxr + */ +@Data +public class CouponExcelVO { + + @ExcelProperty("用户ID") + private Long id; + + @ExcelProperty("优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ExcelProperty("优惠券名称") + private String name; + + @ExcelProperty("优惠券类型id") + private Long couponTypeId; + + @ExcelProperty("优惠券编码") + private String couponCode; + + @ExcelProperty("领用人") + private Long memberId; + + @ExcelProperty("优惠券使用订单id") + private Long useOrderId; + + @ExcelProperty("适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Boolean goodsType; + + @ExcelProperty("适用商品id") + private String goodsIds; + + @ExcelProperty("最小金额") + private BigDecimal atLeast; + + @ExcelProperty("面额") + private BigDecimal money; + + @ExcelProperty("1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ExcelProperty("最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ExcelProperty("优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ExcelProperty("是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ExcelProperty("过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ExcelProperty("是否已提醒") + private Boolean whetherNoticed; + + @ExcelProperty("优惠券状态 1已领用(未使用) 2已使用 3已过期") + private Integer state; + + @ExcelProperty("获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") + private Boolean getType; + + @ExcelProperty("领取时间") + private Date fetchTime; + + @ExcelProperty("使用时间") + private Date useTime; + + @ExcelProperty("可使用的开始时间") + private Date startTime; + + @ExcelProperty("有效期结束时间") + private Date endTime; + + @ExcelProperty("创建时间") + private Date createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java new file mode 100644 index 000000000..a195a61de --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponExportReqVO.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; + +import lombok.*; + +import java.math.BigDecimal; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel(value = "管理后台 - 优惠券 Excel 导出 Request VO", description = "参数和 CouponPageReqVO 是一致的") +@Data +public class CouponExportReqVO { + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ApiModelProperty(value = "优惠券名称") + private String name; + + @ApiModelProperty(value = "优惠券类型id") + private Long couponTypeId; + + @ApiModelProperty(value = "优惠券编码") + private String couponCode; + + @ApiModelProperty(value = "领用人") + private Long memberId; + + @ApiModelProperty(value = "优惠券使用订单id") + private Long useOrderId; + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Boolean goodsType; + + @ApiModelProperty(value = "适用商品id") + private String goodsIds; + + @ApiModelProperty(value = "最小金额") + private BigDecimal atLeast; + + @ApiModelProperty(value = "面额") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "是否已提醒") + private Boolean whetherNoticed; + + @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期") + private Integer state; + + @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") + private Boolean getType; + + @ApiModelProperty(value = "领取时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] fetchTime; + + @ApiModelProperty(value = "使用时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] useTime; + + @ApiModelProperty(value = "可使用的开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] startTime; + + @ApiModelProperty(value = "有效期结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endTime; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java new file mode 100644 index 000000000..9573200a5 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponPageReqVO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; + +import lombok.*; + +import java.math.BigDecimal; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 优惠券分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponPageReqVO extends PageParam { + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ApiModelProperty(value = "优惠券名称") + private String name; + + @ApiModelProperty(value = "优惠券类型id") + private Long couponTypeId; + + @ApiModelProperty(value = "优惠券编码") + private String couponCode; + + @ApiModelProperty(value = "领用人") + private Long memberId; + + @ApiModelProperty(value = "优惠券使用订单id") + private Long useOrderId; + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Boolean goodsType; + + @ApiModelProperty(value = "适用商品id") + private String goodsIds; + + @ApiModelProperty(value = "最小金额") + private BigDecimal atLeast; + + @ApiModelProperty(value = "面额") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "是否已提醒") + private Boolean whetherNoticed; + + @ApiModelProperty(value = "优惠券状态 1已领用(未使用) 2已使用 3已过期") + private Integer state; + + @ApiModelProperty(value = "获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取") + private Boolean getType; + + @ApiModelProperty(value = "领取时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] fetchTime; + + @ApiModelProperty(value = "使用时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] useTime; + + @ApiModelProperty(value = "可使用的开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] startTime; + + @ApiModelProperty(value = "有效期结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endTime; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityRespVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java similarity index 52% rename from yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityRespVO.java rename to yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java index de7ca3af2..9055a36a0 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityRespVO.java +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponRespVO.java @@ -1,16 +1,16 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity.vo; +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; import lombok.*; import java.util.*; import io.swagger.annotations.*; -@ApiModel("管理后台 - 促销活动 Response VO") +@ApiModel("管理后台 - 优惠券 Response VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class ActivityRespVO extends ActivityBaseVO { +public class CouponRespVO extends CouponBaseVO { - @ApiModelProperty(value = "活动编号", required = true) + @ApiModelProperty(value = "用户ID", required = true) private Long id; @ApiModelProperty(value = "创建时间", required = true) diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java new file mode 100644 index 000000000..0c1a741ca --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/coupon/vo/CouponUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 优惠券更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponUpdateReqVO extends CouponBaseVO { + + @ApiModelProperty(value = "用户ID", required = true) + @NotNull(message = "用户ID不能为空") + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java new file mode 100644 index 000000000..a81f53c09 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/CouponTempleteController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete; + +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.*; +import cn.iocoder.yudao.module.coupon.convert.CouponTemplete.CouponTempleteConvert; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; +import cn.iocoder.yudao.module.coupon.service.CouponTemplete.CouponTempleteService; +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.annotations.*; + +import javax.validation.*; +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 优惠券模板") +@RestController +@RequestMapping("/coupon/template") +@Validated +public class CouponTempleteController { + + @Resource + private CouponTempleteService couponTempleteServiceService; + + @PostMapping("/create") + @ApiOperation("创建优惠券模板") + @PreAuthorize("@ss.hasPermission('CouponTemplete::create')") + public CommonResult create(@Valid @RequestBody CouponTempleteCreateReqVO createReqVO) { + return success(couponTempleteServiceService.create(createReqVO)); + } + +// @PutMapping("/update") +// @ApiOperation("更新优惠券模板") +// @PreAuthorize("@ss.hasPermission('CouponTemplete::update')") +// public CommonResult update(@Valid @RequestBody CouponTempleteUpdateReqVO updateReqVO) { +// couponTempleteServiceService.update(updateReqVO); +// return success(true); +// } +// +// @DeleteMapping("/delete") +// @ApiOperation("删除优惠券模板") +// @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) +// @PreAuthorize("@ss.hasPermission('CouponTemplete::delete')") +// public CommonResult delete(@RequestParam("id") Long id) { +// couponTempleteServiceService.delete(id); +// return success(true); +// } +// +// @GetMapping("/get") +// @ApiOperation("获得优惠券模板") +// @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) +// @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") +// public CommonResult get(@RequestParam("id") Long id) { +// CouponTempleteDO couponTempleteDO = couponTempleteServiceService.get(id); +// return success(CouponTempleteConvert.INSTANCE.convert(couponTempleteDO)); +// } +// +// @GetMapping("/list") +// @ApiOperation("获得优惠券模板列表") +// @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) +// @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") +// public CommonResult> getList(@RequestParam("ids") Collection ids) { +// List list = couponTempleteServiceService.getList(ids); +// return success(CouponTempleteConvert.INSTANCE.convertList(list)); +// } +// + @GetMapping("/page") + @ApiOperation("获得优惠券模板分页") + @PreAuthorize("@ss.hasPermission('CouponTemplete::query')") + public CommonResult> getPage(@Valid CouponTempletePageReqVO pageVO) { + PageResult pageResult = couponTempleteServiceService.getPage(pageVO); + return success(CouponTempleteConvert.INSTANCE.convertPage(pageResult)); + } + + + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java new file mode 100644 index 000000000..942abe896 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteBaseVO.java @@ -0,0 +1,172 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import io.swagger.annotations.*; +import javax.validation.constraints.*; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +/** +* 优惠券模板 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class CouponTempleteBaseVO { + + + //基本信息 + + @ApiModelProperty(value = "优惠券名称", required = true) + @NotNull(message = "优惠券名称不能为空") + private String name; + + @ApiModelProperty(value = "名称备注") + private String couponNameRemark; + + @ApiModelProperty(value = "优惠券图片") + private String image; + + /* ============判断适用商品——开始============= */ + + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用", required = true) + @NotNull(message = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用不能为空") + private Integer goodsType; + + @ApiModelProperty(value = "适用商品id") + private String productIds; + + @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛", required = true) + @NotNull(message = "使用门槛0-无门槛 1-有门槛不能为空") + private Boolean hasUseLimit; + + @ApiModelProperty(value = "满多少元使用 0代表无限制", required = true) + @NotNull(message = "满多少元使用 0代表无限制不能为空") + private BigDecimal atLeast; + + + /* ============折扣类型——开始============= */ + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机", required = true) + @NotNull(message = "优惠券类型 reward-满减 discount-折扣 random-随机不能为空") + private String type; + + @ApiModelProperty(value = "发放面额 当type为reward时需要添加") + @NotNull(message = "发放面额 当type为reward时需要添加不能为空") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") + @NotNull(message = "1 =< 折扣 <= 9.9 当type为discount时需要添加不能为空") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") + @NotNull(message = "最多折扣金额 当type为discount时可选择性添加不能为空") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "最低金额 当type为radom时需要添加", required = true) + @NotNull(message = "最低金额 当type为radom时需要添加不能为空") + private BigDecimal minMoney; + + @ApiModelProperty(value = "最大金额 当type为radom时需要添加", required = true) + @NotNull(message = "最大金额 当type为radom时需要添加不能为空") + private BigDecimal maxMoney; + + /* ============折扣类型——结束============= */ + + + /* ============过期类型——开始============= */ + + + @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期", required = true) + @NotNull(message = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期 不能为空") + private Integer validityType; + + @ApiModelProperty(value = "使用开始日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date startUseTime; + + @ApiModelProperty(value = "使用结束日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date endUseTime; + + @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") + @NotNull(message = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效不能为空") + private Integer fixedTerm; + + @ApiModelProperty(value = "有效日期结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date endTime; + + /* ============过期类型——结束============= */ + + + @ApiModelProperty(value = "领取是否无限制0-否 1是", required = true) + @NotNull(message = "是否无限制0-否 1是") + private Boolean whetherLimitless; + + @ApiModelProperty(value = "每人最大领取个数", required = true) + @NotNull(message = "每人最大领取个数不能为空") + private Integer maxFetch; + + @ApiModelProperty(value = "是否开启过期提醒 0-不开启 1-开启", required = true) + @NotNull(message = "是否开启过期提醒0-不开启 1-开启不能为空") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒", required = true) + @NotNull(message = "过期前N天提醒不能为空") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用", required = true) + @NotNull(message = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用不能为空") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否显示", required = true) + @NotNull(message = "是否显示不能为空") + private Integer whetherShow; + + @ApiModelProperty(value = "是否禁止发放0-否 1-是", required = true) + @NotNull(message = "是否禁止发放0-否 1-是不能为空") + private Boolean whetherForbidden; + + /* ============汇总计算——开始============= */ + + + + @ApiModelProperty(value = "使用优惠券购买的商品数量", required = true) + @NotNull(message = "使用优惠券购买的商品数量不能为空") + private Integer orderGoodsNum; + + @ApiModelProperty(value = "订单的优惠总金额", required = true) + @NotNull(message = "订单的优惠总金额不能为空") + private BigDecimal discountOrderMoney; + + @ApiModelProperty(value = "用券总成交额", required = true) + @NotNull(message = "用券总成交额不能为空") + private BigDecimal orderMoney; + + @ApiModelProperty(value = "发放数量", required = true) + @NotNull(message = "发放数量不能为空") + private Integer count; + + @ApiModelProperty(value = "已领取数量", required = true) + @NotNull(message = "已领取数量不能为空") + private Integer leadCount; + + @ApiModelProperty(value = "已使用数量", required = true) + @NotNull(message = "已使用数量不能为空") + private Integer usedCount; + + + /* ============汇总计算——结束============= */ + + + @ApiModelProperty(value = "状态(1进行中2已结束3已关闭)", required = true) + @NotNull(message = "状态(1进行中2已结束-1已关闭)不能为空") + private Integer status; + + + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java new file mode 100644 index 000000000..8e1d9a51a --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 优惠券模板创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTempleteCreateReqVO extends CouponTempleteBaseVO { + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java new file mode 100644 index 000000000..1101f9b61 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExcelVO.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; + +import com.alibaba.excel.annotation.ExcelProperty; + +/** + * 优惠券模板 Excel VO + * + * @author wxr + */ +@Data +public class CouponTempleteExcelVO { + + @ExcelProperty("用户ID") + private Long id; + + @ExcelProperty("优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ExcelProperty("优惠券名称") + private String name; + + @ExcelProperty("名称备注") + private String couponNameRemark; + + @ExcelProperty("优惠券图片") + private String image; + + @ExcelProperty("发放数量") + private Integer count; + + @ExcelProperty("已领取数量") + private Integer leadCount; + + @ExcelProperty("已使用数量") + private Integer usedCount; + + @ExcelProperty("适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Integer goodsType; + + @ExcelProperty("适用商品id") + private String productIds; + + @ExcelProperty("使用门槛0-无门槛 1-有门槛") + private Boolean hasUseLimit; + + @ExcelProperty("满多少元使用 0代表无限制") + private BigDecimal atLeast; + + @ExcelProperty("发放面额 当type为reward时需要添加") + private BigDecimal money; + + @ExcelProperty("1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ExcelProperty("最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ExcelProperty("最低金额 当type为radom时需要添加") + private BigDecimal minMoney; + + @ExcelProperty("最大金额 当type为radom时需要添加") + private BigDecimal maxMoney; + + @ExcelProperty("过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") + private Integer validityType; + + @ExcelProperty("使用开始日期 过期类型1时必填") + private Date startUseTime; + + @ExcelProperty("使用结束日期 过期类型1时必填") + private Date endUseTime; + + @ExcelProperty("当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") + private Integer fixedTerm; + + @ExcelProperty("是否无限制0-否 1是") + private Boolean whetherLimitless; + + @ExcelProperty("每人最大领取个数") + private Integer maxFetch; + + @ExcelProperty("是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ExcelProperty("过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ExcelProperty("优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ExcelProperty("是否显示") + private Integer whetherShow; + + @ExcelProperty("订单的优惠总金额") + private BigDecimal discountOrderMoney; + + @ExcelProperty("用券总成交额") + private BigDecimal orderMoney; + + @ExcelProperty("是否禁止发放0-否 1-是") + private Boolean whetherForbidden; + + @ExcelProperty("使用优惠券购买的商品数量") + private Integer orderGoodsNum; + + @ExcelProperty("状态(1进行中2已结束-1已关闭)") + private Integer status; + + @ExcelProperty("有效日期结束时间") + private Date endTime; + + @ExcelProperty("创建时间") + private Date createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java new file mode 100644 index 000000000..0420e3d9e --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteExportReqVO.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; + +import java.math.BigDecimal; +import java.util.*; +import io.swagger.annotations.*; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel(value = "管理后台 - 优惠券模板 Excel 导出 Request VO", description = "参数和 CouponTempletePageReqVO 是一致的") +@Data +public class CouponTempleteExportReqVO { + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ApiModelProperty(value = "优惠券名称") + private String name; + + @ApiModelProperty(value = "名称备注") + private String couponNameRemark; + + @ApiModelProperty(value = "优惠券图片") + private String image; + + @ApiModelProperty(value = "发放数量") + private Integer count; + + @ApiModelProperty(value = "已领取数量") + private Integer leadCount; + + @ApiModelProperty(value = "已使用数量") + private Integer usedCount; + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Boolean goodsType; + + @ApiModelProperty(value = "适用商品id") + private String productIds; + + @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛") + private Boolean hasUseLimit; + + @ApiModelProperty(value = "满多少元使用 0代表无限制") + private BigDecimal atLeast; + + @ApiModelProperty(value = "发放面额 当type为reward时需要添加") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "最低金额 当type为radom时需要添加") + private BigDecimal minMoney; + + @ApiModelProperty(value = "最大金额 当type为radom时需要添加") + private BigDecimal maxMoney; + + @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") + private Boolean validityType; + + @ApiModelProperty(value = "使用开始日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] startUseTime; + + @ApiModelProperty(value = "使用结束日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endUseTime; + + @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") + private Integer fixedTerm; + + @ApiModelProperty(value = "是否无限制0-否 1是") + private Boolean whetherLimitless; + + @ApiModelProperty(value = "每人最大领取个数") + private Integer maxFetch; + + @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否显示") + private Integer whetherShow; + + @ApiModelProperty(value = "订单的优惠总金额") + private BigDecimal discountOrderMoney; + + @ApiModelProperty(value = "用券总成交额") + private BigDecimal orderMoney; + + @ApiModelProperty(value = "是否禁止发放0-否 1-是") + private Boolean whetherForbidden; + + @ApiModelProperty(value = "使用优惠券购买的商品数量") + private Integer orderGoodsNum; + + @ApiModelProperty(value = "状态(1进行中2已结束-1已关闭)") + private Integer status; + + @ApiModelProperty(value = "有效日期结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endTime; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java new file mode 100644 index 000000000..3ff5710c7 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempletePageReqVO.java @@ -0,0 +1,122 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; + +import java.math.BigDecimal; +import java.util.*; +import io.swagger.annotations.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@ApiModel("管理后台 - 优惠券模板分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTempletePageReqVO extends PageParam { + + @ApiModelProperty(value = "优惠券类型 reward-满减 discount-折扣 random-随机") + private String type; + + @ApiModelProperty(value = "优惠券名称") + private String name; + + @ApiModelProperty(value = "名称备注") + private String couponNameRemark; + + @ApiModelProperty(value = "优惠券图片") + private String image; + + @ApiModelProperty(value = "发放数量") + private Integer count; + + @ApiModelProperty(value = "已领取数量") + private Integer leadCount; + + @ApiModelProperty(value = "已使用数量") + private Integer usedCount; + + @ApiModelProperty(value = "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用") + private Boolean goodsType; + + @ApiModelProperty(value = "适用商品id") + private String productIds; + + @ApiModelProperty(value = "使用门槛0-无门槛 1-有门槛") + private Boolean hasUseLimit; + + @ApiModelProperty(value = "满多少元使用 0代表无限制") + private BigDecimal atLeast; + + @ApiModelProperty(value = "发放面额 当type为reward时需要添加") + private BigDecimal money; + + @ApiModelProperty(value = "1 =< 折扣 <= 9.9 当type为discount时需要添加") + private BigDecimal discount; + + @ApiModelProperty(value = "最多折扣金额 当type为discount时可选择性添加") + private BigDecimal discountLimit; + + @ApiModelProperty(value = "最低金额 当type为radom时需要添加") + private BigDecimal minMoney; + + @ApiModelProperty(value = "最大金额 当type为radom时需要添加") + private BigDecimal maxMoney; + + @ApiModelProperty(value = "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期") + private Boolean validityType; + + @ApiModelProperty(value = "使用开始日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] startUseTime; + + @ApiModelProperty(value = "使用结束日期 过期类型1时必填") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endUseTime; + + @ApiModelProperty(value = "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效") + private Integer fixedTerm; + + @ApiModelProperty(value = "是否无限制0-否 1是") + private Boolean whetherLimitless; + + @ApiModelProperty(value = "每人最大领取个数") + private Integer maxFetch; + + @ApiModelProperty(value = "是否开启过期提醒0-不开启 1-开启") + private Boolean whetherExpireNotice; + + @ApiModelProperty(value = "过期前N天提醒") + private Integer expireNoticeFixedTerm; + + @ApiModelProperty(value = "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用") + private Boolean whetherForbidPreference; + + @ApiModelProperty(value = "是否显示") + private Integer whetherShow; + + @ApiModelProperty(value = "订单的优惠总金额") + private BigDecimal discountOrderMoney; + + @ApiModelProperty(value = "用券总成交额") + private BigDecimal orderMoney; + + @ApiModelProperty(value = "是否禁止发放0-否 1-是") + private Boolean whetherForbidden; + + @ApiModelProperty(value = "使用优惠券购买的商品数量") + private Integer orderGoodsNum; + + @ApiModelProperty(value = "状态(1进行中2已结束-1已关闭)") + private Integer status; + + @ApiModelProperty(value = "有效日期结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] endTime; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private Date[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java new file mode 100644 index 000000000..b65f7c259 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 优惠券模板 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTempleteRespVO extends CouponTempleteBaseVO { + + @ApiModelProperty(value = "用户ID", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java new file mode 100644 index 000000000..1ee8b539d --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/controller/admin/templete/vo/CouponTempleteUpdateReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.coupon.controller.admin.templete.vo; + +import lombok.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 优惠券模板更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CouponTempleteUpdateReqVO extends CouponTempleteBaseVO { + + @ApiModelProperty(value = "用户ID", required = true) + @NotNull(message = "用户ID不能为空") + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java new file mode 100644 index 000000000..a7f37919d --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/CouponTemplete/CouponTempleteConvert.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.coupon.convert.CouponTemplete; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteCreateReqVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExcelVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteRespVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteUpdateReqVO; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + + +/** + * 优惠券模板 Convert + * + * @author wxr + */ +@Mapper +public interface CouponTempleteConvert { + + CouponTempleteConvert INSTANCE = Mappers.getMapper(CouponTempleteConvert.class); + + CouponTempleteDO convert(CouponTempleteCreateReqVO bean); + + CouponTempleteDO convert(CouponTempleteUpdateReqVO bean); + + CouponTempleteRespVO convert(CouponTempleteDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List list); + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java new file mode 100644 index 000000000..e4dab938b --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/convert/coupon/CouponConvert.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.coupon.convert.coupon; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; +import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; + +/** + * 优惠券 Convert + * + * @author wxr + */ +@Mapper +public interface CouponConvert { + + CouponConvert INSTANCE = Mappers.getMapper(CouponConvert.class); + + CouponDO convert(CouponCreateReqVO bean); + + CouponDO convert(CouponUpdateReqVO bean); + + CouponRespVO convert(CouponDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List list); + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java new file mode 100644 index 000000000..e33771808 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/CouponTemplete/CouponTempleteDO.java @@ -0,0 +1,158 @@ +package cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 优惠券模板 DO + * + * @author wxr + */ +@TableName("coupon_templete") +@KeySequence("coupon_templete_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CouponTempleteDO extends BaseDO { + + /** + * 用户ID + */ + @TableId + private Long id; + /** + * 优惠券类型 reward-满减 discount-折扣 random-随机 + */ + private String type; + /** + * 优惠券名称 + */ + private String name; + /** + * 名称备注 + */ + private String couponNameRemark; + /** + * 优惠券图片 + */ + private String image; + /** + * 发放数量 + */ + private Integer count; + /** + * 已领取数量 + */ + private Integer leadCount; + /** + * 已使用数量 + */ + private Integer usedCount; + /** + * 适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用 + */ + private Integer goodsType; + /** + * 适用商品id + */ + private String productIds; + /** + * 使用门槛0-无门槛 1-有门槛 + */ + private Boolean hasUseLimit; + /** + * 满多少元使用 0代表无限制 + */ + private BigDecimal atLeast; + /** + * 发放面额 当type为reward时需要添加 + */ + private BigDecimal money; + /** + * 1 =< 折扣 <= 9.9 当type为discount时需要添加 + */ + private BigDecimal discount; + /** + * 最多折扣金额 当type为discount时可选择性添加 + */ + private BigDecimal discountLimit; + /** + * 最低金额 当type为radom时需要添加 + */ + private BigDecimal minMoney; + /** + * 最大金额 当type为radom时需要添加 + */ + private BigDecimal maxMoney; + /** + * 过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期 + */ + private Integer validityType; + /** + * 使用开始日期 过期类型1时必填 + */ + private Date startUseTime; + /** + * 使用结束日期 过期类型1时必填 + */ + private Date endUseTime; + /** + * 当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效 + */ + private Integer fixedTerm; + /** + * 是否无限制0-否 1是 + */ + private Boolean whetherLimitless; + /** + * 每人最大领取个数 + */ + private Integer maxFetch; + /** + * 是否开启过期提醒0-不开启 1-开启 + */ + private Boolean whetherExpireNotice; + /** + * 过期前N天提醒 + */ + private Integer expireNoticeFixedTerm; + /** + * 优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用 + */ + private Boolean whetherForbidPreference; + /** + * 是否显示 + */ + private Integer whetherShow; + /** + * 订单的优惠总金额 + */ + private BigDecimal discountOrderMoney; + /** + * 用券总成交额 + */ + private BigDecimal orderMoney; + /** + * 是否禁止发放0-否 1-是 + */ + private Boolean whetherForbidden; + /** + * 使用优惠券购买的商品数量 + */ + private Integer orderGoodsNum; + /** + * 状态(1进行中2已结束-1已关闭) + */ + private Integer status; + /** + * 有效日期结束时间 + */ + private Date endTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java new file mode 100644 index 000000000..70e3aae20 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/dataobject/coupon/CouponDO.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.coupon.dal.dataobject.coupon; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.*; + +/** + * 优惠券 DO + * + * @author wxr + */ +@TableName("coupon") +@KeySequence("coupon_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CouponDO extends BaseDO { + + /** + * 用户ID + */ + @TableId + private Long id; + /** + * 优惠券类型 reward-满减 discount-折扣 random-随机 + */ + private String type; + /** + * 优惠券名称 + */ + private String name; + /** + * 优惠券类型id + */ + private Long couponTypeId; + /** + * 优惠券编码 + */ + private String couponCode; + /** + * 领用人 + */ + private Long memberId; + /** + * 优惠券使用订单id + */ + private Long useOrderId; + /** + * 适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用 + */ + private Boolean goodsType; + /** + * 适用商品id + */ + private String goodsIds; + /** + * 最小金额 + */ + private BigDecimal atLeast; + /** + * 面额 + */ + private BigDecimal money; + /** + * 1 =< 折扣 <= 9.9 当type为discount时需要添加 + */ + private BigDecimal discount; + /** + * 最多折扣金额 当type为discount时可选择性添加 + */ + private BigDecimal discountLimit; + /** + * 优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用 + */ + private Boolean whetherForbidPreference; + /** + * 是否开启过期提醒0-不开启 1-开启 + */ + private Boolean whetherExpireNotice; + /** + * 过期前N天提醒 + */ + private Integer expireNoticeFixedTerm; + /** + * 是否已提醒 + */ + private Boolean whetherNoticed; + /** + * 优惠券状态 1已领用(未使用) 2已使用 3已过期 + */ + private Integer state; + /** + * 获取方式1订单2.直接领取3.活动领取 4转赠 5分享获取 + */ + private Boolean getType; + /** + * 领取时间 + */ + private Date fetchTime; + /** + * 使用时间 + */ + private Date useTime; + /** + * 可使用的开始时间 + */ + private Date startTime; + /** + * 有效期结束时间 + */ + private Date endTime; + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java new file mode 100644 index 000000000..3243ce17a --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/CouponTemplete/CouponTempleteMapper.java @@ -0,0 +1,98 @@ +package cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExportReqVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempletePageReqVO; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; +import org.apache.ibatis.annotations.Mapper; + + +/** + * 优惠券模板 Mapper + * + * @author wxr + */ +@Mapper +public interface CouponTempleteMapper extends BaseMapperX { + + default PageResult selectPage(CouponTempletePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CouponTempleteDO::getType, reqVO.getType()) + .likeIfPresent(CouponTempleteDO::getName, reqVO.getName()) + .eqIfPresent(CouponTempleteDO::getCouponNameRemark, reqVO.getCouponNameRemark()) + .eqIfPresent(CouponTempleteDO::getImage, reqVO.getImage()) + .eqIfPresent(CouponTempleteDO::getCount, reqVO.getCount()) + .eqIfPresent(CouponTempleteDO::getLeadCount, reqVO.getLeadCount()) + .eqIfPresent(CouponTempleteDO::getUsedCount, reqVO.getUsedCount()) + .eqIfPresent(CouponTempleteDO::getGoodsType, reqVO.getGoodsType()) + .eqIfPresent(CouponTempleteDO::getProductIds, reqVO.getProductIds()) + .eqIfPresent(CouponTempleteDO::getHasUseLimit, reqVO.getHasUseLimit()) + .eqIfPresent(CouponTempleteDO::getAtLeast, reqVO.getAtLeast()) + .eqIfPresent(CouponTempleteDO::getMoney, reqVO.getMoney()) + .eqIfPresent(CouponTempleteDO::getDiscount, reqVO.getDiscount()) + .eqIfPresent(CouponTempleteDO::getDiscountLimit, reqVO.getDiscountLimit()) + .eqIfPresent(CouponTempleteDO::getMinMoney, reqVO.getMinMoney()) + .eqIfPresent(CouponTempleteDO::getMaxMoney, reqVO.getMaxMoney()) + .eqIfPresent(CouponTempleteDO::getValidityType, reqVO.getValidityType()) + .betweenIfPresent(CouponTempleteDO::getStartUseTime, reqVO.getStartUseTime()) + .betweenIfPresent(CouponTempleteDO::getEndUseTime, reqVO.getEndUseTime()) + .eqIfPresent(CouponTempleteDO::getFixedTerm, reqVO.getFixedTerm()) + .eqIfPresent(CouponTempleteDO::getWhetherLimitless, reqVO.getWhetherLimitless()) + .eqIfPresent(CouponTempleteDO::getMaxFetch, reqVO.getMaxFetch()) + .eqIfPresent(CouponTempleteDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) + .eqIfPresent(CouponTempleteDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) + .eqIfPresent(CouponTempleteDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) + .eqIfPresent(CouponTempleteDO::getWhetherShow, reqVO.getWhetherShow()) + .eqIfPresent(CouponTempleteDO::getDiscountOrderMoney, reqVO.getDiscountOrderMoney()) + .eqIfPresent(CouponTempleteDO::getOrderMoney, reqVO.getOrderMoney()) + .eqIfPresent(CouponTempleteDO::getWhetherForbidden, reqVO.getWhetherForbidden()) + .eqIfPresent(CouponTempleteDO::getOrderGoodsNum, reqVO.getOrderGoodsNum()) + .eqIfPresent(CouponTempleteDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(CouponTempleteDO::getEndTime, reqVO.getEndTime()) + .betweenIfPresent(CouponTempleteDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponTempleteDO::getId)); + } + + default List selectList(CouponTempleteExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(CouponTempleteDO::getType, reqVO.getType()) + .likeIfPresent(CouponTempleteDO::getName, reqVO.getName()) + .eqIfPresent(CouponTempleteDO::getCouponNameRemark, reqVO.getCouponNameRemark()) + .eqIfPresent(CouponTempleteDO::getImage, reqVO.getImage()) + .eqIfPresent(CouponTempleteDO::getCount, reqVO.getCount()) + .eqIfPresent(CouponTempleteDO::getLeadCount, reqVO.getLeadCount()) + .eqIfPresent(CouponTempleteDO::getUsedCount, reqVO.getUsedCount()) + .eqIfPresent(CouponTempleteDO::getGoodsType, reqVO.getGoodsType()) + .eqIfPresent(CouponTempleteDO::getProductIds, reqVO.getProductIds()) + .eqIfPresent(CouponTempleteDO::getHasUseLimit, reqVO.getHasUseLimit()) + .eqIfPresent(CouponTempleteDO::getAtLeast, reqVO.getAtLeast()) + .eqIfPresent(CouponTempleteDO::getMoney, reqVO.getMoney()) + .eqIfPresent(CouponTempleteDO::getDiscount, reqVO.getDiscount()) + .eqIfPresent(CouponTempleteDO::getDiscountLimit, reqVO.getDiscountLimit()) + .eqIfPresent(CouponTempleteDO::getMinMoney, reqVO.getMinMoney()) + .eqIfPresent(CouponTempleteDO::getMaxMoney, reqVO.getMaxMoney()) + .eqIfPresent(CouponTempleteDO::getValidityType, reqVO.getValidityType()) + .betweenIfPresent(CouponTempleteDO::getStartUseTime, reqVO.getStartUseTime()) + .betweenIfPresent(CouponTempleteDO::getEndUseTime, reqVO.getEndUseTime()) + .eqIfPresent(CouponTempleteDO::getFixedTerm, reqVO.getFixedTerm()) + .eqIfPresent(CouponTempleteDO::getWhetherLimitless, reqVO.getWhetherLimitless()) + .eqIfPresent(CouponTempleteDO::getMaxFetch, reqVO.getMaxFetch()) + .eqIfPresent(CouponTempleteDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) + .eqIfPresent(CouponTempleteDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) + .eqIfPresent(CouponTempleteDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) + .eqIfPresent(CouponTempleteDO::getWhetherShow, reqVO.getWhetherShow()) + .eqIfPresent(CouponTempleteDO::getDiscountOrderMoney, reqVO.getDiscountOrderMoney()) + .eqIfPresent(CouponTempleteDO::getOrderMoney, reqVO.getOrderMoney()) + .eqIfPresent(CouponTempleteDO::getWhetherForbidden, reqVO.getWhetherForbidden()) + .eqIfPresent(CouponTempleteDO::getOrderGoodsNum, reqVO.getOrderGoodsNum()) + .eqIfPresent(CouponTempleteDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(CouponTempleteDO::getEndTime, reqVO.getEndTime()) + .betweenIfPresent(CouponTempleteDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponTempleteDO::getId)); + } + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java new file mode 100644 index 000000000..8c9014694 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/dal/mysql/coupon/CouponMapper.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.coupon.dal.mysql.coupon; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; + +/** + * 优惠券 Mapper + * + * @author wxr + */ +@Mapper +public interface CouponMapper extends BaseMapperX { + + default PageResult selectPage(CouponPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CouponDO::getType, reqVO.getType()) + .likeIfPresent(CouponDO::getName, reqVO.getName()) + .eqIfPresent(CouponDO::getCouponTypeId, reqVO.getCouponTypeId()) + .eqIfPresent(CouponDO::getCouponCode, reqVO.getCouponCode()) + .eqIfPresent(CouponDO::getMemberId, reqVO.getMemberId()) + .eqIfPresent(CouponDO::getUseOrderId, reqVO.getUseOrderId()) + .eqIfPresent(CouponDO::getGoodsType, reqVO.getGoodsType()) + .eqIfPresent(CouponDO::getGoodsIds, reqVO.getGoodsIds()) + .eqIfPresent(CouponDO::getAtLeast, reqVO.getAtLeast()) + .eqIfPresent(CouponDO::getMoney, reqVO.getMoney()) + .eqIfPresent(CouponDO::getDiscount, reqVO.getDiscount()) + .eqIfPresent(CouponDO::getDiscountLimit, reqVO.getDiscountLimit()) + .eqIfPresent(CouponDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) + .eqIfPresent(CouponDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) + .eqIfPresent(CouponDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) + .eqIfPresent(CouponDO::getWhetherNoticed, reqVO.getWhetherNoticed()) + .eqIfPresent(CouponDO::getState, reqVO.getState()) + .eqIfPresent(CouponDO::getGetType, reqVO.getGetType()) + .betweenIfPresent(CouponDO::getFetchTime, reqVO.getFetchTime()) + .betweenIfPresent(CouponDO::getUseTime, reqVO.getUseTime()) + .betweenIfPresent(CouponDO::getStartTime, reqVO.getStartTime()) + .betweenIfPresent(CouponDO::getEndTime, reqVO.getEndTime()) + .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponDO::getId)); + } + + default List selectList(CouponExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(CouponDO::getType, reqVO.getType()) + .likeIfPresent(CouponDO::getName, reqVO.getName()) + .eqIfPresent(CouponDO::getCouponTypeId, reqVO.getCouponTypeId()) + .eqIfPresent(CouponDO::getCouponCode, reqVO.getCouponCode()) + .eqIfPresent(CouponDO::getMemberId, reqVO.getMemberId()) + .eqIfPresent(CouponDO::getUseOrderId, reqVO.getUseOrderId()) + .eqIfPresent(CouponDO::getGoodsType, reqVO.getGoodsType()) + .eqIfPresent(CouponDO::getGoodsIds, reqVO.getGoodsIds()) + .eqIfPresent(CouponDO::getAtLeast, reqVO.getAtLeast()) + .eqIfPresent(CouponDO::getMoney, reqVO.getMoney()) + .eqIfPresent(CouponDO::getDiscount, reqVO.getDiscount()) + .eqIfPresent(CouponDO::getDiscountLimit, reqVO.getDiscountLimit()) + .eqIfPresent(CouponDO::getWhetherForbidPreference, reqVO.getWhetherForbidPreference()) + .eqIfPresent(CouponDO::getWhetherExpireNotice, reqVO.getWhetherExpireNotice()) + .eqIfPresent(CouponDO::getExpireNoticeFixedTerm, reqVO.getExpireNoticeFixedTerm()) + .eqIfPresent(CouponDO::getWhetherNoticed, reqVO.getWhetherNoticed()) + .eqIfPresent(CouponDO::getState, reqVO.getState()) + .eqIfPresent(CouponDO::getGetType, reqVO.getGetType()) + .betweenIfPresent(CouponDO::getFetchTime, reqVO.getFetchTime()) + .betweenIfPresent(CouponDO::getUseTime, reqVO.getUseTime()) + .betweenIfPresent(CouponDO::getStartTime, reqVO.getStartTime()) + .betweenIfPresent(CouponDO::getEndTime, reqVO.getEndTime()) + .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CouponDO::getId)); + } + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java new file mode 100644 index 000000000..6743fd141 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/package-info.java @@ -0,0 +1,6 @@ +/** + * coupon模块,主要负责麦一些优惠券的额增删 + * + * 1. Controller URL:以 /coumon/ 开头,避免和其它 Module 冲突 + */ +package cn.iocoder.yudao.module.coupon; diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java new file mode 100644 index 000000000..0527d11b7 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteService.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.coupon.service.CouponTemplete; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.*; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; + +/** + * 优惠券模板 Service 接口 + * + * @author wxr + */ +public interface CouponTempleteService { + + /** + * 创建优惠券模板 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long create(@Valid CouponTempleteCreateReqVO createReqVO); + + /** + * 更新优惠券模板 + * + * @param updateReqVO 更新信息 + */ + void update(@Valid CouponTempleteUpdateReqVO updateReqVO); + + /** + * 删除优惠券模板 + * + * @param id 编号 + */ + void delete(Long id); + + /** + * 获得优惠券模板 + * + * @param id 编号 + * @return 优惠券模板 + */ + CouponTempleteDO get(Long id); + + /** + * 获得优惠券模板列表 + * + * @param ids 编号 + * @return 优惠券模板列表 + */ + List getList(Collection ids); + + /** + * 获得优惠券模板分页 + * + * @param pageReqVO 分页查询 + * @return 优惠券模板分页 + */ + PageResult getPage(CouponTempletePageReqVO pageReqVO); + + /** + * 获得优惠券模板列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 优惠券模板列表 + */ + List getList(CouponTempleteExportReqVO exportReqVO); + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java new file mode 100644 index 000000000..6f07c4b2a --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/CouponTemplete/CouponTempleteServiceImpl.java @@ -0,0 +1,134 @@ +package cn.iocoder.yudao.module.coupon.service.CouponTemplete; + +import cn.iocoder.yudao.module.CouponTemplete.enums.CouponTypeEnum; +import cn.iocoder.yudao.module.CouponTemplete.enums.CouponValidityTypeEnum; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteCreateReqVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteExportReqVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempletePageReqVO; +import cn.iocoder.yudao.module.coupon.controller.admin.templete.vo.CouponTempleteUpdateReqVO; +import cn.iocoder.yudao.module.coupon.convert.CouponTemplete.CouponTempleteConvert; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; +import cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete.CouponTempleteMapper; +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; + +import java.util.*; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + + + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.CouponTemplete.enums.ErrorCodeConstants.*; + +/** + * 优惠券模板 Service 实现类 + * + * @author wxr + */ +@Service +@Validated +public class CouponTempleteServiceImpl implements CouponTempleteService { + + @Resource + private CouponTempleteMapper couponTempleteMapper; + + @Override + public Long create(CouponTempleteCreateReqVO createReqVO) { + // 插入 + CouponTempleteDO couponTempleteDO = CouponTempleteConvert.INSTANCE.convert(createReqVO); + /* 验证类型、判断必填*/ + checkCouponType(createReqVO); + + /*验证过期类型、判断必填*/ + checkValidityType(createReqVO); + + + + couponTempleteMapper.insert(couponTempleteDO); + // 返回 + return couponTempleteDO.getId(); + } + + /*确认优惠券类型*/ + private void checkValidityType(CouponTempleteCreateReqVO createReqVO) { + Integer validtyType = createReqVO.getValidityType(); + + if(CouponValidityTypeEnum.TIME_RANGE_EXPIRTED.getStatus().equals(validtyType)){ + if(createReqVO.getStartUseTime() == null||createReqVO.getEndUseTime() == null){ + throw exception(START_END_TIME_NOT_NULL); + } + }else{ + if(createReqVO.getFixedTerm() == null){ + throw exception(FIXED_TERM_NOT_NULL); + } + } + } + + private void checkCouponType(CouponTempleteCreateReqVO createReqVO) { + + String couponType = createReqVO.getType(); + //当type=reward时候,需要添加 + if(couponType.equals(CouponTypeEnum.REWARD.getName())){ + if(createReqVO.getMoney()==null){ + throw exception(MONEY_NOT_NULL); + } + }else if(couponType.equals(CouponTypeEnum.DISCOUNT.getName())){ + if(createReqVO.getDiscount()==null){ + throw exception(DISCOUNT_NOT_NULL); + } + if(createReqVO.getDiscountLimit()==null){ + throw exception(DISCOUNT_LIMIT_NOT_NULL); + } + }else if (couponType.equals(CouponTypeEnum.RANDOW.getName())){ + //当type为radom时需要添加不能为空 + if(createReqVO.getMinMoney()==null||createReqVO.getMaxMoney()==null){ + throw exception(MIN_MAX_NOT_NULL); + } + } + } + + @Override + public void update(CouponTempleteUpdateReqVO updateReqVO) { + // 校验存在 + this.validateExists(updateReqVO.getId()); + // 更新 + CouponTempleteDO updateObj = CouponTempleteConvert.INSTANCE.convert(updateReqVO); + couponTempleteMapper.updateById(updateObj); + } + + @Override + public void delete(Long id) { + // 校验存在 + this.validateExists(id); + // 删除 + couponTempleteMapper.deleteById(id); + } + + private void validateExists(Long id) { + if (couponTempleteMapper.selectById(id) == null) { + throw exception(COUPON_TEMPLETE_NOT_EXISTS); + } + } + + @Override + public CouponTempleteDO get(Long id) { + return couponTempleteMapper.selectById(id); + } + + @Override + public List getList(Collection ids) { + return couponTempleteMapper.selectBatchIds(ids); + } + + @Override + public PageResult getPage(CouponTempletePageReqVO pageReqVO) { + return couponTempleteMapper.selectPage(pageReqVO); + } + + @Override + public List getList(CouponTempleteExportReqVO exportReqVO) { + return couponTempleteMapper.selectList(exportReqVO); + } + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java new file mode 100644 index 000000000..6a9d31520 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponService.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.coupon.service.coupon; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; +import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +/** + * 优惠券 Service 接口 + * + * @author wxr + */ +public interface CouponService { + + /** + * 创建优惠券 + * + * @param templateId 优惠券模板id + * @return 编号 + */ + Long create(Long templateId); + + /** + * 更新优惠券 + * + * @param updateReqVO 更新信息 + */ + void update(@Valid CouponUpdateReqVO updateReqVO); + + /** + * 删除优惠券 + * + * @param id 编号 + */ + void delete(Long id); + + /** + * 获得优惠券 + * + * @param id 编号 + * @return 优惠券 + */ + CouponDO get(Long id); + + /** + * 获得优惠券列表 + * + * @param ids 编号 + * @return 优惠券列表 + */ + List getList(Collection ids); + + /** + * 获得优惠券分页 + * + * @param pageReqVO 分页查询 + * @return 优惠券分页 + */ + PageResult getPage(CouponPageReqVO pageReqVO); + + /** + * 获得优惠券列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 优惠券列表 + */ + List getList(CouponExportReqVO exportReqVO); + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java new file mode 100644 index 000000000..0a1737a31 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/java/cn/iocoder/yudao/module/coupon/service/coupon/CouponServiceImpl.java @@ -0,0 +1,107 @@ +package cn.iocoder.yudao.module.coupon.service.coupon; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.coupon.dal.dataobject.CouponTemplete.CouponTempleteDO; +import cn.iocoder.yudao.module.coupon.dal.mysql.CouponTemplete.CouponTempleteMapper; +import io.micrometer.core.instrument.Counter; +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; + +import java.util.*; +import cn.iocoder.yudao.module.coupon.controller.admin.coupon.vo.*; +import cn.iocoder.yudao.module.coupon.dal.dataobject.coupon.CouponDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.module.coupon.convert.coupon.CouponConvert; +import cn.iocoder.yudao.module.coupon.dal.mysql.coupon.CouponMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.CouponTemplete.enums.ErrorCodeConstants.COUPON_NOT_EXISTS; + +/** + * 优惠券 Service 实现类 + * + * @author wxr + */ +@Service +@Validated +public class CouponServiceImpl implements CouponService { + + @Resource + private CouponMapper couponMapper; + + @Resource + private CouponTempleteMapper couponTempleteMapper; + + public Long create(CouponCreateReqVO createReqVO) { + // 插入 + CouponDO couponDO = CouponConvert.INSTANCE.convert(createReqVO); + couponMapper.insert(couponDO); + // 返回 + return couponDO.getId(); + } + + + /** + *todo 获取用户id收获优惠券 + *@author:wxr + *@date:2022-08-13 3:11 + *@Description + */ + @Override + public Long create(Long templateId) { + Long userid = SecurityFrameworkUtils.getLoginUserId(); + CouponDO couponDO = CouponDO.builder().memberId(userid).build(); + CouponTempleteDO couponTempleteDO = couponTempleteMapper.selectById(templateId); + //todo 缺少判空 + BeanUtil.copyProperties(couponTempleteDO,couponTempleteDO); + couponMapper.insert(couponDO); + return couponDO.getId(); + } + + @Override + public void update(CouponUpdateReqVO updateReqVO) { + // 校验存在 + this.validateExists(updateReqVO.getId()); + // 更新 + CouponDO updateObj = CouponConvert.INSTANCE.convert(updateReqVO); + couponMapper.updateById(updateObj); + } + + @Override + public void delete(Long id) { + // 校验存在 + this.validateExists(id); + // 删除 + couponMapper.deleteById(id); + } + + private void validateExists(Long id) { + if (couponMapper.selectById(id) == null) { + throw exception(COUPON_NOT_EXISTS); + } + } + + @Override + public CouponDO get(Long id) { + return couponMapper.selectById(id); + } + + @Override + public List getList(Collection ids) { + return couponMapper.selectBatchIds(ids); + } + + @Override + public PageResult getPage(CouponPageReqVO pageReqVO) { + return couponMapper.selectPage(pageReqVO); + } + + @Override + public List getList(CouponExportReqVO exportReqVO) { + return couponMapper.selectList(exportReqVO); + } + +} diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml new file mode 100644 index 000000000..b7db75a77 --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/CouponTemplete/CouponTempleteMapper.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml new file mode 100644 index 000000000..a5e70cf5d --- /dev/null +++ b/yudao-module-mall/yudao-module-coupon-biz/src/main/resources/mapper/coupon/CouponMapper.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/yudao-module-mall/yudao-module-market-api/pom.xml b/yudao-module-mall/yudao-module-market-api/pom.xml index fef3428fc..9517c343f 100644 --- a/yudao-module-mall/yudao-module-market-api/pom.xml +++ b/yudao-module-mall/yudao-module-market-api/pom.xml @@ -21,6 +21,13 @@ cn.iocoder.boot yudao-common + + + + org.springframework.boot + spring-boot-starter-validation + true + diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java new file mode 100644 index 000000000..532a66dc2 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.market.api.price; + +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; + +/** + * 价格 API 接口 + * + * @author 芋道源码 + */ +public interface PriceApi { + + /** + * 计算商品的价格 + * + * @param calculateReqDTO 价格请求 + * @return 价格相应 + */ + PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO); + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java new file mode 100644 index 000000000..4cc019651 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateReqDTO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.market.api.price.dto; + +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 价格计算 Request DTO + * + * @author 芋道源码 + */ +@Data +public class PriceCalculateReqDTO { + + /** + * 用户编号 + * + * 对应 MemberUserDO 的 id 编号 + */ + private Long userId; + + /** + * 优惠劵编号 + */ + private Long couponId; + + /** + * 商品 SKU 数组 + */ + @NotNull(message = "商品数组不能为空") + private List items; + + /** + * 商品 SKU + */ + @Data + public static class Item { + + /** + * SKU 编号 + */ + @NotNull(message = "商品 SKU 编号不能为空") + private Long skuId; + + /** + * SKU 数量 + */ + @NotNull(message = "商品 SKU 数量不能为空") + @Min(value = 0L, message = "商品 SKU 数量必须大于等于 0") // 可传递 0 数量,用于购物车未选中的情况 + private Integer count; + + } + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java new file mode 100644 index 000000000..dc4f10a10 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java @@ -0,0 +1,202 @@ +package cn.iocoder.yudao.module.market.api.price.dto; + +import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum; +import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum; +import lombok.Data; + +import java.util.List; + +/** + * 价格计算 Response DTO + * + * @author 芋道源码 + */ +@Data +public class PriceCalculateRespDTO { + + /** + * 订单 + */ + private Order order; + + /** + * 商品 SKU 数组 + */ + private List items; + + /** + * 营销活动数组 + * + * 只对应 {@link #items} 商品匹配的活动 + */ + private List promotions; + + /** + * 订单 + */ + @Data + public static class Order { + + /** + * 商品原价(总),单位:分 + * + * 基于 {@link Item#getTotalOriginalPrice()} 求和 + */ + private Integer skuOriginalPrice; + /** + * 商品优惠(总),单位:分 + * + * 基于 {@link Item#getTotalPromotionPrice()} 求和 + */ + private Integer skuPromotionPrice; + /** + * 订单优惠(总),单位:分 + * + * 例如说:满减折扣;不包括优惠劵、商品优惠 + */ + private Integer orderPromotionPrice; + /** + * 运费金额,单位:分 + */ + private Integer deliveryPrice; + /** + * 应付金额(总),单位:分 + * + * = {@link #skuOriginalPrice} + * + {@link #deliveryPrice} + * - {@link #skuPromotionPrice} + * - {@link #orderPromotionPrice} + */ + // * - {@link #couponPrice} // TODO 芋艿:靠营销表记录 + private Integer payPrice; + + // ========== 营销基本信息 ========== + /** + * 优惠劵编号 + */ + private Long couponId; +// /** +// * 优惠劵减免金额,单位:分 +// * +// * // TODO 芋艿:靠营销表记录 +// */ +// private Integer couponPrice; + + } + + /** + * 商品 SKU + */ + @Data + public static class Item extends PriceCalculateReqDTO.Item { + + /** + * 商品原价(单),单位:分 + * + * 对应 ProductSkuDO 的 price 字段 + */ + private Integer originalPrice; + /** + * 商品原价(总),单位:分 + * + * = {@link #originalPrice} * {@link #getCount()} + */ + private Integer totalOriginalPrice; + /** + * 商品级优惠(总),单位:分 + * + * 例如说“限时折扣”:商品原价的 8 折;商品原价的减 50 元 + */ + private Integer totalPromotionPrice; + /** + * 最终购买金额(总),单位:分。 + * + * = {@link #totalOriginalPrice} + * - {@link #totalPromotionPrice} + */ + private Integer totalPresentPrice; + /** + * 最终购买金额(单),单位:分。 + * + * = {@link #totalPresentPrice} / {@link #getCount()} + */ + private Integer presentPrice; + /** + * 应付金额(总),单位:分 + */ + private Integer totalPayPrice; + + } + + /** + * 营销活动 + */ + @Data + public static class Promotion { + + /** + * 营销编号 + * + * 例如说:营销活动的编号、优惠劵的编号 + */ + private Long id; + /** + * 营销类型 + * + * 枚举 {@link PromotionTypeEnum} + */ + private Integer type; + /** + * 营销级别 + * + * 枚举 {@link PromotionLevelEnum} + */ + private Integer level; + /** + * 匹配的商品 SKU 数组 + */ + private List items; + /** + * 计算时的原价(总),单位:分 + */ + private Integer totalOriginalPrice; + /** + * 计算时的优惠(总),单位:分 + */ + private Integer totalPromotionPrice; + /** + * 是否满足优惠条件 + */ + private Boolean meet; + /** + * 满足条件的提示 + * + * 如果 {@link #meet} = true 满足,则提示“圣诞价:省 150.00 元” + * 如果 {@link #meet} = false 不满足,则提示“购满 85 元,可减 40 元” + */ + private String meetTip; + + /** + * 匹配的商品 SKU + */ + @Data + public static class Item { + + /** + * 商品 SKU 编号 + */ + private Long skuId; + /** + * 计算时的原价(总),单位:分 + */ + private Integer totalOriginalPrice; + /** + * 计算时的优惠(总),单位:分 + */ + private Integer totalPromotionPrice; + + } + + } + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityStatusEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityStatusEnum.java deleted file mode 100644 index a02b0269c..000000000 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityStatusEnum.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.market.enums.activity; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; - -import java.util.Arrays; - -/** - * 促销活动状态枚举 - */ -public enum MarketActivityStatusEnum implements IntArrayValuable { - - WAIT(10, "未开始"), - RUN(20, "进行中"), - END(30, "已结束"), - /** - * 1. WAIT、RUN、END 可以转换成 INVALID 状态。 - * 2. INVALID 只可以转换成 DELETED 状态。 - */ - INVALID(40, "已撤销"), - DELETED(50, "已删除"), - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MarketActivityStatusEnum::getValue).toArray(); - - /** - * 状态值 - */ - private final Integer value; - /** - * 状态名 - */ - private final String name; - - MarketActivityStatusEnum(Integer value, String name) { - this.value = value; - this.name = name; - } - - public Integer getValue() { - return value; - } - - public String getName() { - return name; - } - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityTypeEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityTypeEnum.java deleted file mode 100644 index 0413dba66..000000000 --- a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityTypeEnum.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.market.enums.activity; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; - -import java.util.Arrays; - -/** - * 促销活动类型枚举 - */ -public enum MarketActivityTypeEnum implements IntArrayValuable { - - TIME_LIMITED_DISCOUNT(1, "限时折扣"), - FULL_PRIVILEGE(2, "满减送"), - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MarketActivityTypeEnum::getValue).toArray(); - - /** - * 类型值 - */ - private final Integer value; - /** - * 类型名 - */ - private final String name; - - MarketActivityTypeEnum(Integer value, String name) { - this.value = value; - this.name = name; - } - - public Integer getValue() { - return value; - } - - public String getName() { - return name; - } - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java new file mode 100644 index 000000000..9577841aa --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionActivityStatusEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.market.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 促销活动的状态枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum PromotionActivityStatusEnum implements IntArrayValuable { + + WAIT(10, "未开始"), + RUN(20, "进行中"), + END(30, "已结束"), + CLOSE(40, "已关闭"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionActivityStatusEnum::getStatus).toArray(); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java new file mode 100644 index 000000000..4df0e6957 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionConditionTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.market.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 营销的条件类型枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum PromotionConditionTypeEnum implements IntArrayValuable { + + PRICE(10, "满 N 元"), + COUNT(20, "满 N 件"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionConditionTypeEnum::getType).toArray(); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java new file mode 100644 index 000000000..25e3f33c2 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.market.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 营销的级别枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PromotionLevelEnum implements IntArrayValuable { + + ORDER(1, "订单级"), + SKU(2, "商品级"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionLevelEnum::getLevel).toArray(); + + /** + * 级别值 + */ + private final Integer level; + /** + * 类型名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java new file mode 100644 index 000000000..e1792ee67 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionProductScopeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.market.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 营销的商品范围枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PromotionProductScopeEnum implements IntArrayValuable { + + ALL(1, "全部商品参与"), + SPU(2, "指定商品参与"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray(); + + /** + * 范围值 + */ + private final Integer scope; + /** + * 范围名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java new file mode 100644 index 000000000..f3f5cc882 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.market.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 营销类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PromotionTypeEnum implements IntArrayValuable { + + DISCOUNT(1, "限时折扣"), + REWARD(2, "满减送"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray(); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/MarketTestController.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/MarketTestController.java deleted file mode 100644 index 49b83b6c9..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/MarketTestController.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Api(tags = "管理后台 - 营销") -@RestController -@RequestMapping("/market/test") -@Validated -public class MarketTestController { - - @GetMapping("/get") - @ApiOperation("获取 market 信息") - public CommonResult get() { - return success("true"); - } - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/ActivityController.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/ActivityController.java deleted file mode 100644 index dac4211a6..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/ActivityController.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.annotations.*; -import javax.validation.*; -import java.util.*; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; -import cn.iocoder.yudao.module.market.convert.activity.ActivityConvert; -import cn.iocoder.yudao.module.market.service.activity.ActivityService; - -@Api(tags = "管理后台 - 促销活动") -@RestController -@RequestMapping("/market/activity") -@Validated -public class ActivityController { - - @Resource - private ActivityService activityService; - - @PostMapping("/create") - @ApiOperation("创建促销活动") - @PreAuthorize("@ss.hasPermission('market:activity:create')") - public CommonResult createActivity(@Valid @RequestBody ActivityCreateReqVO createReqVO) { - return success(activityService.createActivity(createReqVO)); - } - - @PutMapping("/update") - @ApiOperation("更新促销活动") - @PreAuthorize("@ss.hasPermission('market:activity:update')") - public CommonResult updateActivity(@Valid @RequestBody ActivityUpdateReqVO updateReqVO) { - activityService.updateActivity(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @ApiOperation("删除促销活动") - @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('market:activity:delete')") - public CommonResult deleteActivity(@RequestParam("id") Long id) { - activityService.deleteActivity(id); - return success(true); - } - - @GetMapping("/get") - @ApiOperation("获得促销活动") - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('market:activity:query')") - public CommonResult getActivity(@RequestParam("id") Long id) { - ActivityDO activity = activityService.getActivity(id); - return success(ActivityConvert.INSTANCE.convert(activity)); - } - - @GetMapping("/list") - @ApiOperation("获得促销活动列表") - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) - @PreAuthorize("@ss.hasPermission('market:activity:query')") - public CommonResult> getActivityList(@RequestParam("ids") Collection ids) { - List list = activityService.getActivityList(ids); - return success(ActivityConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @ApiOperation("获得促销活动分页") - @PreAuthorize("@ss.hasPermission('market:activity:query')") - public CommonResult> getActivityPage(@Valid ActivityPageReqVO pageVO) { - PageResult pageResult = activityService.getActivityPage(pageVO); - return success(ActivityConvert.INSTANCE.convertPage(pageResult)); - } - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityBaseVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityBaseVO.java deleted file mode 100644 index 3ae5cd679..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityBaseVO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.market.enums.activity.MarketActivityStatusEnum; -import cn.iocoder.yudao.module.market.enums.activity.MarketActivityTypeEnum; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -/** -* 促销活动 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ -@Data -public class ActivityBaseVO { - - @ApiModelProperty(value = "活动标题", required = true) - @NotNull(message = "活动标题不能为空") - private String title; - - @ApiModelProperty(value = "活动类型", required = true) - @NotNull(message = "活动类型不能为空") - @InEnum(MarketActivityTypeEnum.class) - private Integer activityType; - - @ApiModelProperty(value = "活动状态", required = true) - @NotNull(message = "活动状态不能为空") - @InEnum(MarketActivityStatusEnum.class) - private Integer status; - - @ApiModelProperty(value = "开始时间", required = true) - @NotNull(message = "开始时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date startTime; - - @ApiModelProperty(value = "结束时间", required = true) - @NotNull(message = "结束时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date endTime; - - @ApiModelProperty(value = "失效时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date invalidTime; - - @ApiModelProperty(value = "删除时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date deleteTime; - - @ApiModelProperty(value = "限制折扣字符串,使用 JSON 序列化成字符串存储") - private String timeLimitedDiscount; - - @ApiModelProperty(value = "限制折扣字符串,使用 JSON 序列化成字符串存储") - private String fullPrivilege; - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityCreateReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityCreateReqVO.java deleted file mode 100644 index 0ca112709..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityCreateReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -/** - * @author xia - */ -@ApiModel("管理后台 - 促销活动创建 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ActivityCreateReqVO extends ActivityBaseVO { - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityPageReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityPageReqVO.java deleted file mode 100644 index 9543d8fee..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityPageReqVO.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.market.enums.activity.MarketActivityStatusEnum; -import cn.iocoder.yudao.module.market.enums.activity.MarketActivityTypeEnum; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel("管理后台 - 促销活动分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ActivityPageReqVO extends PageParam { - - @ApiModelProperty(value = "活动标题") - private String title; - - @ApiModelProperty(value = "活动类型") - @InEnum(MarketActivityTypeEnum.class) - private Integer activityType; - - @ApiModelProperty(value = "活动状态") - @InEnum(MarketActivityStatusEnum.class) - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "开始时间") - private Date[] startTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "结束时间") - private Date[] endTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "失效时间") - private Date[] invalidTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "删除时间") - private Date[] deleteTime; - - @ApiModelProperty(value = "限制折扣字符串,使用 JSON 序列化成字符串存储") - private String timeLimitedDiscount; - - @ApiModelProperty(value = "限制折扣字符串,使用 JSON 序列化成字符串存储") - private String fullPrivilege; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityUpdateReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityUpdateReqVO.java deleted file mode 100644 index 1db24f259..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityUpdateReqVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.market.controller.admin.activity.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 促销活动更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ActivityUpdateReqVO extends ActivityBaseVO { - - @ApiModelProperty(value = "活动编号", required = true) - @NotNull(message = "活动编号不能为空") - private Long id; - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java new file mode 100644 index 000000000..a3f145f26 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/discount/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 占位 + */ +package cn.iocoder.yudao.module.market.controller.admin.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/activity/ActivityConvert.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/activity/ActivityConvert.java deleted file mode 100644 index 64ba73975..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/activity/ActivityConvert.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.market.convert.activity; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; - -/** - * 促销活动 Convert - * - * @author 芋道源码 - */ -@Mapper -public interface ActivityConvert { - - ActivityConvert INSTANCE = Mappers.getMapper(ActivityConvert.class); - - ActivityDO convert(ActivityCreateReqVO bean); - - ActivityDO convert(ActivityUpdateReqVO bean); - - ActivityRespVO convert(ActivityDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java new file mode 100644 index 000000000..787492406 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/discount/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 占位 + */ +package cn.iocoder.yudao.module.market.convert.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/activity/ActivityDO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/activity/ActivityDO.java deleted file mode 100644 index 13dcbf67c..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/activity/ActivityDO.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.market.dal.dataobject.activity; - -import lombok.*; -import java.util.*; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 促销活动 DO - * - * @author 芋道源码 - */ -@TableName("market_activity") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ActivityDO extends BaseDO { - - /** - * 活动编号 - */ - @TableId - private Long id; - /** - * 活动标题 - */ - private String title; - /** - * 活动类型MarketActivityTypeEnum - */ - private Integer activityType; - /** - * 活动状态MarketActivityStatusEnum - */ - private Integer status; - /** - * 开始时间 - */ - private Date startTime; - /** - * 结束时间 - */ - private Date endTime; - /** - * 失效时间 - */ - private Date invalidTime; - /** - * 删除时间 - */ - private Date deleteTime; - /** - * 限制折扣字符串,使用 JSON 序列化成字符串存储 - */ - private String timeLimitedDiscount; - /** - * 限制折扣字符串,使用 JSON 序列化成字符串存储 - */ - private String fullPrivilege; - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java new file mode 100644 index 000000000..8780546f6 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountActivityDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.market.dal.dataobject.discount; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.market.enums.common.PromotionActivityStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 限时折扣活动 DO + * + * 一个活动下,可以有 {@link DiscountProductDO} 商品; + * 一个商品,在指定时间段内,只能属于一个活动; + * + * @author 芋道源码 + */ +@TableName(value = "promotion_discount_activity", autoResultMap = true) +@KeySequence("promotion_discount_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class DiscountActivityDO extends BaseDO { + + /** + * 活动编号,主键自增 + */ + @TableId + private Long id; + /** + * 活动标题 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link PromotionActivityStatusEnum} + */ + private Integer status; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java new file mode 100644 index 000000000..cd73a19a5 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/discount/DiscountProductDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.market.dal.dataobject.discount; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 限时折扣商品 DO + * + * @author 芋道源码 + */ +@TableName(value = "promotion_discount_product", autoResultMap = true) +@KeySequence("promotion_discount_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class DiscountProductDO extends BaseDO { + + /** + * 编号,主键自增 + */ + @TableId + private Long id; + /** + * 限时折扣活动的编号 + * + * 关联 {@link DiscountActivityDO#getId()} + */ + private Long activityId; + /** + * 商品 SPU 编号 + * + * 关联 ProductSpuDO 的 id 编号 + */ + private Long spuId; + /** + * 商品 SKU 编号 + * + * 关联 ProductSkuDO 的 id 编号 + */ + private Long skuId; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 销售价格,单位:分 + * + * 冗余 ProductSkuDO 的 price 字段 + */ + private Integer originalPrice; + /** + * 优惠价格,单位:分 + */ + private Integer promotionPrice; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java new file mode 100644 index 000000000..85cd8c955 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/reward/RewardActivityDO.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.market.dal.dataobject.reward; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.market.enums.common.PromotionActivityStatusEnum; +import cn.iocoder.yudao.module.market.enums.common.PromotionConditionTypeEnum; +import cn.iocoder.yudao.module.market.enums.common.PromotionProductScopeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; +import java.util.List; + +/** + * 满减送活动 DO + * + * @author 芋道源码 + */ +@TableName(value = "promotion_reward_activity", autoResultMap = true) +@KeySequence("promotion_reward_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class RewardActivityDO extends BaseDO { + + /** + * 活动编号,主键自增 + */ + @TableId + private Long id; + /** + * 活动标题 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link PromotionActivityStatusEnum} + */ + private Integer status; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 备注 + */ + private String remark; + /** + * 条件类型 + * + * 枚举 {@link PromotionConditionTypeEnum} + */ + private Integer conditionType; + /** + * 商品范围 + * + * 枚举 {@link PromotionProductScopeEnum} + */ + private Integer productScope; + /** + * 商品 SPU 编号的数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List spuIds; + /** + * 优惠规则的数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List rules; + + /** + * 优惠规则 + */ + @Data + public static class Rule { + + /** + * 优惠门槛 + * + * 1. 满 N 元,单位:分 + * 2. 满 N 件 + */ + private Integer limit; + /** + * 优惠价格,单位:分 + */ + private Integer promotionPrice; + /** + * 是否包邮 + */ + private Boolean freeDelivery; + /** + * 赠送的积分 + */ + private Integer integral; + /** + * 赠送的优惠劵编号的数组 + */ + private List couponIds; + /** + * 赠送的优惠卷数量的数组 + */ + private List couponCounts; + + } + + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/activity/ActivityMapper.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/activity/ActivityMapper.java deleted file mode 100644 index feb3fb122..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/activity/ActivityMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.market.dal.mysql.activity; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; - -/** - * 促销活动 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface ActivityMapper extends BaseMapperX { - - default PageResult selectPage(ActivityPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(ActivityDO::getTitle, reqVO.getTitle()) - .eqIfPresent(ActivityDO::getActivityType, reqVO.getActivityType()) - .eqIfPresent(ActivityDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ActivityDO::getStartTime, reqVO.getStartTime()) - .betweenIfPresent(ActivityDO::getEndTime, reqVO.getEndTime()) - .betweenIfPresent(ActivityDO::getInvalidTime, reqVO.getInvalidTime()) - .betweenIfPresent(ActivityDO::getDeleteTime, reqVO.getDeleteTime()) - .eqIfPresent(ActivityDO::getTimeLimitedDiscount, reqVO.getTimeLimitedDiscount()) - .eqIfPresent(ActivityDO::getFullPrivilege, reqVO.getFullPrivilege()) - .betweenIfPresent(ActivityDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ActivityDO::getId)); - } - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java new file mode 100644 index 000000000..f8de42cd9 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/discount/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 占位 + */ +package cn.iocoder.yudao.module.market.dal.mysql.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java index 2efde4ec7..92b9c698d 100644 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java @@ -1,8 +1,8 @@ /** - * market 模块,我们放营销业务。 + * promotion 模块,我们放营销业务。 * 例如说:营销活动、banner、优惠券等等 * - * 1. Controller URL:以 /market/ 开头,避免和其它 Module 冲突 - * 2. DataObject 表名:以 market_ 开头,方便在数据库中区分 + * 1. Controller URL:以 /promotion/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 promotion_ 开头,方便在数据库中区分 */ package cn.iocoder.yudao.module.market; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityService.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityService.java deleted file mode 100644 index 1d5e27857..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityService.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.market.service.activity; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -/** - * 促销活动 Service 接口 - * - * @author 芋道源码 - */ -public interface ActivityService { - - /** - * 创建促销活动 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createActivity(@Valid ActivityCreateReqVO createReqVO); - - /** - * 更新促销活动 - * - * @param updateReqVO 更新信息 - */ - void updateActivity(@Valid ActivityUpdateReqVO updateReqVO); - - /** - * 删除促销活动 - * - * @param id 编号 - */ - void deleteActivity(Long id); - - /** - * 获得促销活动 - * - * @param id 编号 - * @return 促销活动 - */ - ActivityDO getActivity(Long id); - - /** - * 获得促销活动列表 - * - * @param ids 编号 - * @return 促销活动列表 - */ - List getActivityList(Collection ids); - - /** - * 获得促销活动分页 - * - * @param pageReqVO 分页查询 - * @return 促销活动分页 - */ - PageResult getActivityPage(ActivityPageReqVO pageReqVO); - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImpl.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImpl.java deleted file mode 100644 index 57bb9af53..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.market.service.activity; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; - -import java.util.*; -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import cn.iocoder.yudao.module.market.convert.activity.ActivityConvert; -import cn.iocoder.yudao.module.market.dal.mysql.activity.ActivityMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.*; - -/** - * 促销活动 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class ActivityServiceImpl implements ActivityService { - - @Resource - private ActivityMapper activityMapper; - - @Override - public Long createActivity(ActivityCreateReqVO createReqVO) { - // 插入 - ActivityDO activity = ActivityConvert.INSTANCE.convert(createReqVO); - activityMapper.insert(activity); - // 返回 - return activity.getId(); - } - - @Override - public void updateActivity(ActivityUpdateReqVO updateReqVO) { - // 校验存在 - this.validateActivityExists(updateReqVO.getId()); - // 更新 - ActivityDO updateObj = ActivityConvert.INSTANCE.convert(updateReqVO); - activityMapper.updateById(updateObj); - } - - @Override - public void deleteActivity(Long id) { - // 校验存在 - this.validateActivityExists(id); - // 删除 - activityMapper.deleteById(id); - } - - private void validateActivityExists(Long id) { - if (activityMapper.selectById(id) == null) { - throw exception(ACTIVITY_NOT_EXISTS); - } - } - - @Override - public ActivityDO getActivity(Long id) { - return activityMapper.selectById(id); - } - - @Override - public List getActivityList(Collection ids) { - return activityMapper.selectBatchIds(ids); - } - - @Override - public PageResult getActivityPage(ActivityPageReqVO pageReqVO) { - return activityMapper.selectPage(pageReqVO); - } - -} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java new file mode 100644 index 000000000..f52febd44 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 占位 + */ +package cn.iocoder.yudao.module.market.service.discount; diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java new file mode 100644 index 000000000..6b8663d1e --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.market.service.price; + +import cn.iocoder.yudao.module.market.api.price.PriceApi; +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import org.springframework.stereotype.Service; + +/** + * 价格 API 实现类 + * + * TODO 完善注释 + * + * @author TODO + */ +@Service +public class PriceApiImpl implements PriceApi { + + @Override + public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { + // TODO fixme:实现逻辑 + return new PriceCalculateRespDTO(); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImplTest.java b/yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImplTest.java deleted file mode 100644 index b292251a4..000000000 --- a/yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImplTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package cn.iocoder.yudao.module.market.service.activity; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.market.controller.admin.activity.vo.*; -import cn.iocoder.yudao.module.market.dal.dataobject.activity.ActivityDO; -import cn.iocoder.yudao.module.market.dal.mysql.activity.ActivityMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.springframework.context.annotation.Import; - -import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static org.junit.jupiter.api.Assertions.*; - -/** -* {@link ActivityServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ -@Import(ActivityServiceImpl.class) -public class ActivityServiceImplTest extends BaseDbUnitTest { - - @Resource - private ActivityServiceImpl activityService; - - @Resource - private ActivityMapper activityMapper; - - @Test - public void testCreateActivity_success() { - // 准备参数 - ActivityCreateReqVO reqVO = randomPojo(ActivityCreateReqVO.class); - - // 调用 - Long activityId = activityService.createActivity(reqVO); - // 断言 - assertNotNull(activityId); - // 校验记录的属性是否正确 - ActivityDO activity = activityMapper.selectById(activityId); - assertPojoEquals(reqVO, activity); - } - - @Test - public void testUpdateActivity_success() { - // mock 数据 - ActivityDO dbActivity = randomPojo(ActivityDO.class); - activityMapper.insert(dbActivity);// @Sql: 先插入出一条存在的数据 - // 准备参数 - ActivityUpdateReqVO reqVO = randomPojo(ActivityUpdateReqVO.class, o -> { - o.setId(dbActivity.getId()); // 设置更新的 ID - }); - - // 调用 - activityService.updateActivity(reqVO); - // 校验是否更新正确 - ActivityDO activity = activityMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, activity); - } - - @Test - public void testUpdateActivity_notExists() { - // 准备参数 - ActivityUpdateReqVO reqVO = randomPojo(ActivityUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> activityService.updateActivity(reqVO), ACTIVITY_NOT_EXISTS); - } - - @Test - public void testDeleteActivity_success() { - // mock 数据 - ActivityDO dbActivity = randomPojo(ActivityDO.class); - activityMapper.insert(dbActivity);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbActivity.getId(); - - // 调用 - activityService.deleteActivity(id); - // 校验数据不存在了 - assertNull(activityMapper.selectById(id)); - } - - @Test - public void testDeleteActivity_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> activityService.deleteActivity(id), ACTIVITY_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetActivityPage() { - // mock 数据 - ActivityDO dbActivity = randomPojo(ActivityDO.class, o -> { // 等会查询到 - o.setTitle(null); - o.setActivityType(null); - o.setStatus(null); - o.setStartTime(null); - o.setEndTime(null); - o.setInvalidTime(null); - o.setDeleteTime(null); - o.setTimeLimitedDiscount(null); - o.setFullPrivilege(null); - o.setCreateTime(null); - }); - activityMapper.insert(dbActivity); - // 测试 title 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setTitle(null))); - // 测试 activityType 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setActivityType(null))); - // 测试 status 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setStatus(null))); - // 测试 startTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setStartTime(null))); - // 测试 endTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setEndTime(null))); - // 测试 invalidTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setInvalidTime(null))); - // 测试 deleteTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setDeleteTime(null))); - // 测试 timeLimitedDiscount 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setTimeLimitedDiscount(null))); - // 测试 fullPrivilege 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setFullPrivilege(null))); - // 测试 createTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setCreateTime(null))); - // 准备参数 - ActivityPageReqVO reqVO = new ActivityPageReqVO(); - reqVO.setTitle(null); - reqVO.setActivityType(null); - reqVO.setStatus(null); - reqVO.setStartTime(null); - reqVO.setEndTime(null); - reqVO.setInvalidTime(null); - reqVO.setDeleteTime(null); - reqVO.setTimeLimitedDiscount(null); - reqVO.setFullPrivilege(null); - reqVO.setCreateTime(null); - - // 调用 - PageResult pageResult = activityService.getActivityPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbActivity, pageResult.getList().get(0)); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetActivityList() { - // mock 数据 - ActivityDO dbActivity = randomPojo(ActivityDO.class, o -> { // 等会查询到 - o.setTitle(null); - o.setActivityType(null); - o.setStatus(null); - o.setStartTime(null); - o.setEndTime(null); - o.setInvalidTime(null); - o.setDeleteTime(null); - o.setTimeLimitedDiscount(null); - o.setFullPrivilege(null); - o.setCreateTime(null); - }); - activityMapper.insert(dbActivity); - // 测试 title 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setTitle(null))); - // 测试 activityType 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setActivityType(null))); - // 测试 status 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setStatus(null))); - // 测试 startTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setStartTime(null))); - // 测试 endTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setEndTime(null))); - // 测试 invalidTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setInvalidTime(null))); - // 测试 deleteTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setDeleteTime(null))); - // 测试 timeLimitedDiscount 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setTimeLimitedDiscount(null))); - // 测试 fullPrivilege 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setFullPrivilege(null))); - // 测试 createTime 不匹配 - activityMapper.insert(cloneIgnoreId(dbActivity, o -> o.setCreateTime(null))); - } - -} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java new file mode 100644 index 000000000..b3915407f --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.product.api.sku; + +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; + +import java.util.Collection; +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface ProductSkuApi { + + + /** + * 根据skuId列表 查询sku信息 + * + * @param skuIds sku ID列表 + * @return sku信息列表 + */ + List getSkusByIds(Collection skuIds); + + /** + * 批量扣减sku库存 + * + * @param batchReqDTO sku库存信息列表 + */ + void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO); +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java new file mode 100644 index 000000000..c0cee91ba --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuDecrementStockBatchReqDTO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.product.api.sku.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SkuDecrementStockBatchReqDTO { + + + private List items; + + @Data + public static class Item { + + /** + * 商品 SPU 编号,自增 + */ + private Long productId; + + /** + * 商品 SKU 编号,自增 + */ + private Long skuId; + + /** + * 数量 + */ + private Integer count; + + } + + public static SkuDecrementStockBatchReqDTO of(List items) { + return new SkuDecrementStockBatchReqDTO(items); + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java new file mode 100644 index 000000000..f9d349e48 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.product.api.sku.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Data +public class SkuInfoRespDTO { + + /** + * 商品 SKU 编号,自增 + */ + private Long id; + /** + * 商品 SKU 名字 + */ + private String name; + /** + * SPU 编号 + */ + private Long spuId; + + /** + * 规格值数组,JSON 格式 + */ + private List properties; + /** + * 销售价格,单位:分 + */ + private Integer price; + /** + * 市场价,单位:分 + */ + private Integer marketPrice; + /** + * 成本价,单位:分 + */ + private Integer costPrice; + /** + * SKU 的条形码 + */ + private String barCode; + /** + * 图片地址 + */ + private String picUrl; + /** + * SKU 状态 + *

+ * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 库存 + */ + private Integer stock; + /** + * 预警预存 + */ + private Integer warnStock; + /** + * 商品重量,单位:kg 千克 + */ + private Double weight; + /** + * 商品体积,单位:m^3 平米 + */ + private Double volume; + + /** + * 商品属性 + */ + @Data + public static class Property { + + /** + * 属性编号 + */ + private Long propertyId; + /** + * 属性值编号 + */ + private Long valueId; + + } + + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java new file mode 100644 index 000000000..5dc2bf4cf --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.api.spu; + +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; + +import java.util.Collection; +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface ProductSpuApi { + + + /** + * 根据spuId列表 查询spu信息 + * + * @param spuIds spu ID列表 + * @return spu信息列表 + */ + List getSpusByIds(Collection spuIds); +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java new file mode 100644 index 000000000..6d0206b7d --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.product.api.spu.dto; + +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import lombok.Data; + +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Data +public class SpuInfoRespDTO { + + /** + * 商品 SPU 编号,自增 + */ + private Long id; + + // ========== 基本信息 ========= + + /** + * 商品名称 + */ + private String name; + /** + * 商品编码 + */ + private String code; + /** + * 商品卖点 + */ + private String sellPoint; + /** + * 商品详情 + */ + private String description; + /** + * 商品分类编号 + */ + private Long categoryId; + /** + * 商品品牌编号 + */ + private Long brandId; + /** + * 商品图片的数组 + *

+ * 1. 第一张图片将作为商品主图,支持同时上传多张图; + * 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片; + * 3. 至少 1 张,最多上传 10 张 + */ + private List picUrls; + /** + * 商品视频 + */ + private String videoUrl; + + /** + * 排序字段 + */ + private Integer sort; + /** + * 商品状态 + *

+ * 枚举 {@link ProductSpuStatusEnum} + */ + private Integer status; + + // ========== SKU 相关字段 ========= + + /** + * 规格类型 + *

+ * 枚举 {@link ProductSpuSpecTypeEnum} + */ + private Integer specType; + /** + * 最小价格,单位使用:分 + *

+ * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最小值 + */ + private Integer minPrice; + /** + * 最大价格,单位使用:分 + *

+ * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最大值 + */ + private Integer maxPrice; + /** + * 市场价,单位使用:分 + *

+ * 基于其对应的 {@link SkuInfoRespDTO#getMarketPrice()} 最大值 + */ + private Integer marketPrice; + /** + * 总库存 + *

+ * 基于其对应的 {@link SkuInfoRespDTO#getStock()} 求和 + */ + private Integer totalStock; + /** + * 是否展示库存 + */ + private Boolean showStock; + + // ========== 统计相关字段 ========= + + /** + * 商品销量 + */ + private Integer salesCount; + /** + * 虚拟销量 + */ + private Integer virtualSalesCount; + /** + * 商品点击量 + */ + private Integer clickCount; + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java index e328d2512..801e2dd51 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java @@ -3,30 +3,38 @@ package cn.iocoder.yudao.module.product.enums; import cn.iocoder.yudao.framework.common.exception.ErrorCode; /** - * product 错误码枚举类 - *

+ * Product 错误码枚举类 + * * product 系统,使用 1-008-000-000 段 */ public interface ErrorCodeConstants { - // ========== 商品分类相关 1008001000============ + // ========== 商品分类相关 1008001000 ============ ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在"); ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1008001001, "父分类不存在"); - ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001002, "存在子分类,无法删除"); + ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类"); + ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除"); + ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用"); + ErrorCode CATEGORY_LEVEL_ERROR = new ErrorCode(1008001005, "商品分类不正确,原因:必须使用第三级的商品分类下"); - // ========== 品牌相关编号 1008002000 ========== + // ========== 商品品牌相关编号 1008002000 ========== ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在"); + ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌不存在"); + ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在"); - // ========== 规格名称 1008003000 ========== + // ========== 商品规格名称 1008003000 ========== ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "规格名称不存在"); // ========== 规格值 1008004000 ========== ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "规格值不存在"); - // ========== 商品spu 1008005000 ========== - ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品spu不存在"); + // ========== 商品 SPU 1008005000 ========== + ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在"); + + // ========== 商品 SKU 1008006000 ========== + ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在"); + ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复"); + ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU,其规格数必须一致"); + ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU,必须不重复"); - // ========== 商品sku 1008006000 ========== - ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品sku不存在"); - ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品sku的属性组合存在重复"); } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentAuditStatusEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentAuditStatusEnum.java new file mode 100644 index 000000000..276839daf --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentAuditStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.enums.comment; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 商品评论的审批状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum ProductCommentAuditStatusEnum implements IntArrayValuable { + + NONE(1, "待审核"), + APPROVE(2, "审批通过"), + REJECT(2, "审批不通过"),; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductCommentAuditStatusEnum::getStatus).toArray(); + + /** + * 审批状态 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/delivery/DeliveryTypeEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/delivery/DeliveryTypeEnum.java new file mode 100644 index 000000000..da322ff24 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/delivery/DeliveryTypeEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.enums.delivery; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 配送方式枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum DeliveryTypeEnum implements IntArrayValuable { + + // TODO 芋艿:英文单词,需要再想下; + EXPRESS(1, "快递发货"), + USER(2, "用户自提"),; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DeliveryTypeEnum::getMode).toArray(); + + /** + * 配送方式 + */ + private final Integer mode; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/group/ProductGroupStyleEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/group/ProductGroupStyleEnum.java new file mode 100644 index 000000000..c5e55e8e4 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/group/ProductGroupStyleEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.enums.group; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 商品分组的样式枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum ProductGroupStyleEnum implements IntArrayValuable { + + ONE(1, "每列一个"), + TWO(2, "每列两个"), + THREE(2, "每列三个"),; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductGroupStyleEnum::getStyle).toArray(); + + /** + * 列表样式 + */ + private final Integer style; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java new file mode 100644 index 000000000..30ece744d --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.product.enums.spu; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 商品 SPU 规格类型 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum ProductSpuSpecTypeEnum implements IntArrayValuable { + + RECYCLE(1, "统一规格"), + DISABLE(2, "多规格"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuSpecTypeEnum::getType).toArray(); + + /** + * 规格 + */ + private final Integer type; + /** + * 规格名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java new file mode 100644 index 000000000..1757f1e49 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.enums.spu; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 商品 SPU 状态 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum ProductSpuStatusEnum implements IntArrayValuable { + + RECYCLE(-1, "回收站"), + DISABLE(0, "下架"), + ENABLE(1, "上架"),; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStyle).toArray(); + + /** + * 状态 + */ + private final Integer style; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/pom.xml b/yudao-module-mall/yudao-module-product-biz/pom.xml index a06f8937c..bb9a36f94 100644 --- a/yudao-module-mall/yudao-module-product-biz/pom.xml +++ b/yudao-module-mall/yudao-module-product-biz/pom.xml @@ -2,13 +2,12 @@ - 4.0.0 cn.iocoder.boot yudao-module-mall ${revision} - + 4.0.0 yudao-module-product-biz jar @@ -18,7 +17,6 @@ 例如:品牌、商品分类、spu、sku等功能。 - cn.iocoder.boot @@ -31,10 +29,6 @@ cn.iocoder.boot yudao-spring-boot-starter-biz-operatelog - - cn.iocoder.boot - yudao-spring-boot-starter-biz-weixin - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant @@ -64,4 +58,4 @@ - \ No newline at end of file + diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java new file mode 100644 index 000000000..162453c3c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.product.api; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java new file mode 100644 index 000000000..22636826b --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.product.api.sku; + +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * todo 注释 + */ +@Service +public class ProductSkuApiImpl implements ProductSkuApi { + + @Override + public List getSkusByIds(Collection skuIds) { + return null; + } + + @Override + public void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO) { + + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java new file mode 100644 index 000000000..8f651f395 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.product.api.spu; + +import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * todo 注释 + */ +@Service +public class ProductSpuApiImpl implements ProductSpuApi { + + @Override + public List getSpusByIds(Collection spuIds) { + return null; + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/BrandController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java similarity index 53% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/BrandController.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java index 0702d206c..2227afeaf 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/BrandController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java @@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.product.controller.admin.brand; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; -import cn.iocoder.yudao.module.product.convert.brand.BrandConvert; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; -import cn.iocoder.yudao.module.product.service.brand.BrandService; +import cn.iocoder.yudao.module.product.convert.brand.ProductBrandConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; @@ -16,34 +14,32 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.IOException; +import java.util.Comparator; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; -@Api(tags = "管理后台 - 品牌") +@Api(tags = "管理后台 - 商品品牌") @RestController @RequestMapping("/product/brand") @Validated -public class BrandController { +public class ProductBrandController { @Resource - private BrandService brandService; + private ProductBrandService brandService; @PostMapping("/create") @ApiOperation("创建品牌") @PreAuthorize("@ss.hasPermission('product:brand:create')") - public CommonResult createBrand(@Valid @RequestBody BrandCreateReqVO createReqVO) { + public CommonResult createBrand(@Valid @RequestBody ProductBrandCreateReqVO createReqVO) { return success(brandService.createBrand(createReqVO)); } @PutMapping("/update") @ApiOperation("更新品牌") @PreAuthorize("@ss.hasPermission('product:brand:update')") - public CommonResult updateBrand(@Valid @RequestBody BrandUpdateReqVO updateReqVO) { + public CommonResult updateBrand(@Valid @RequestBody ProductBrandUpdateReqVO updateReqVO) { brandService.updateBrand(updateReqVO); return success(true); } @@ -61,29 +57,26 @@ public class BrandController { @ApiOperation("获得品牌") @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @PreAuthorize("@ss.hasPermission('product:brand:query')") - public CommonResult getBrand(@RequestParam("id") Long id) { - BrandDO brand = brandService.getBrand(id); - return success(BrandConvert.INSTANCE.convert(brand)); + public CommonResult getBrand(@RequestParam("id") Long id) { + ProductBrandDO brand = brandService.getBrand(id); + return success(ProductBrandConvert.INSTANCE.convert(brand)); } @GetMapping("/page") @ApiOperation("获得品牌分页") @PreAuthorize("@ss.hasPermission('product:brand:query')") - public CommonResult> getBrandPage(@Valid BrandPageReqVO pageVO) { - PageResult pageResult = brandService.getBrandPage(pageVO); - return success(BrandConvert.INSTANCE.convertPage(pageResult)); + public CommonResult> getBrandPage(@Valid ProductBrandPageReqVO pageVO) { + PageResult pageResult = brandService.getBrandPage(pageVO); + return success(ProductBrandConvert.INSTANCE.convertPage(pageResult)); } - @GetMapping("/export-excel") - @ApiOperation("导出品牌 Excel") - @PreAuthorize("@ss.hasPermission('product:brand:export')") - @OperateLog(type = EXPORT) - public void exportBrandExcel(@Valid BrandExportReqVO exportReqVO, - HttpServletResponse response) throws IOException { - List list = brandService.getBrandList(exportReqVO); - // 导出 Excel - List datas = BrandConvert.INSTANCE.convertList02(list); - ExcelUtils.write(response, "品牌.xls", "数据", BrandExcelVO.class, datas); + @GetMapping("/list") + @ApiOperation("获得品牌列表") + @PreAuthorize("@ss.hasPermission('product:brand:query')") + public CommonResult> getBrandList(@Valid ProductBrandListReqVO listVO) { + List list = brandService.getBrandList(listVO); + list.sort(Comparator.comparing(ProductBrandDO::getSort)); + return success(ProductBrandConvert.INSTANCE.convertList(list)); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExcelVO.java deleted file mode 100644 index 261b69ea5..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExcelVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.brand.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; - -import com.alibaba.excel.annotation.ExcelProperty; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - - -/** - * 品牌 Excel VO - * - * @author 芋道源码 - */ -@Data -public class BrandExcelVO { - - @ExcelProperty("品牌编号") - private Long id; - - @ExcelProperty("分类编号") - private Long categoryId; - - @ExcelProperty("品牌名称") - private String name; - - @ExcelProperty("品牌图片") - private String bannerUrl; - - @ExcelProperty("品牌排序") - private Integer sort; - - @ExcelProperty("品牌描述") - private String description; - - @ExcelProperty(value = "状态", converter = DictConvert.class) - @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中 - private Integer status; - - @ExcelProperty("创建时间") - private Date createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExportReqVO.java deleted file mode 100644 index 1385613a6..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExportReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.brand.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 品牌 Excel 导出 Request VO", description = "参数和 BrandPageReqVO 是一致的") -@Data -public class BrandExportReqVO { - - @ApiModelProperty(value = "分类编号", example = "1") - private Long categoryId; - - @ApiModelProperty(value = "品牌名称", example = "芋道") - private String name; - - @ApiModelProperty(value = "状态", example = "0") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandPageReqVO.java deleted file mode 100644 index c706e51ca..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.brand.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel("管理后台 - 品牌分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BrandPageReqVO extends PageParam { - - @ApiModelProperty(value = "分类编号", example = "1") - private Long categoryId; - - @ApiModelProperty(value = "品牌名称", example = "芋道") - private String name; - - @ApiModelProperty(value = "状态", example = "0") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java similarity index 62% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandBaseVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java index 57c5a390c..fa5ecd2d1 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java @@ -1,20 +1,16 @@ package cn.iocoder.yudao.module.product.controller.admin.brand.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; /** -* 品牌 Base VO,提供给添加、修改、详细的子 VO 使用 +* 商品品牌 Base VO,提供给添加、修改、详细的子 VO 使用 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 */ @Data -public class BrandBaseVO { - - @ApiModelProperty(value = "分类编号", required = true, example = "1") - @NotNull(message = "分类编号不能为空") - private Long categoryId; +public class ProductBrandBaseVO { @ApiModelProperty(value = "品牌名称", required = true, example = "芋道") @NotNull(message = "品牌名称不能为空") @@ -22,9 +18,10 @@ public class BrandBaseVO { @ApiModelProperty(value = "品牌图片", required = true) @NotNull(message = "品牌图片不能为空") - private String bannerUrl; + private String picUrl; - @ApiModelProperty(value = "品牌排序", example = "1") + @ApiModelProperty(value = "品牌排序", required = true, example = "1") + @NotNull(message = "品牌排序不能为空") private Integer sort; @ApiModelProperty(value = "品牌描述", example = "描述") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandCreateReqVO.java similarity index 54% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandCreateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandCreateReqVO.java index 3a6f844fb..74024929f 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandCreateReqVO.java @@ -1,14 +1,12 @@ package cn.iocoder.yudao.module.product.controller.admin.brand.vo; import lombok.*; -import java.util.*; import io.swagger.annotations.*; -import javax.validation.constraints.*; -@ApiModel("管理后台 - 品牌创建 Request VO") +@ApiModel("管理后台 - 商品品牌创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BrandCreateReqVO extends BrandBaseVO { +public class ProductBrandCreateReqVO extends ProductBrandBaseVO { } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java new file mode 100644 index 000000000..5367e2dfe --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.product.controller.admin.brand.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel(value = "管理后台 - 商品品牌分页 Request VO") +@Data +public class ProductBrandListReqVO { + + @ApiModelProperty(value = "品牌名称", example = "芋道") + private String name; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java similarity index 67% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryPageReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java index 1cf83bbbe..3b3d829de 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.product.controller.admin.category.vo; +package cn.iocoder.yudao.module.product.controller.admin.brand.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.annotations.ApiModel; @@ -12,16 +12,16 @@ import java.util.Date; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@ApiModel("管理后台 - 商品分类分页 Request VO") +@ApiModel("管理后台 - 商品品牌分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class CategoryPageReqVO extends PageParam { +public class ProductBrandPageReqVO extends PageParam { - @ApiModelProperty(value = "分类名称", example = "办公文具") + @ApiModelProperty(value = "品牌名称", example = "芋道") private String name; - @ApiModelProperty(value = "开启状态", example = "0") + @ApiModelProperty(value = "状态", example = "0", notes = "参考 CommonStatusEnum 枚举") private Integer status; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandRespVO.java similarity index 60% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandRespVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandRespVO.java index 5e010b4d0..f577a6c26 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandRespVO.java @@ -1,14 +1,18 @@ package cn.iocoder.yudao.module.product.controller.admin.brand.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; @ApiModel("管理后台 - 品牌 Response VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BrandRespVO extends BrandBaseVO { +public class ProductBrandRespVO extends ProductBrandBaseVO { @ApiModelProperty(value = "品牌编号", required = true, example = "1") private Long id; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandUpdateReqVO.java similarity index 75% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandUpdateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandUpdateReqVO.java index 287157f0e..54d610207 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandUpdateReqVO.java @@ -1,15 +1,14 @@ package cn.iocoder.yudao.module.product.controller.admin.brand.vo; import lombok.*; -import java.util.*; import io.swagger.annotations.*; import javax.validation.constraints.*; -@ApiModel("管理后台 - 品牌更新 Request VO") +@ApiModel("管理后台 - 商品品牌更新 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BrandUpdateReqVO extends BrandBaseVO { +public class ProductBrandUpdateReqVO extends ProductBrandBaseVO { @ApiModelProperty(value = "品牌编号", required = true, example = "1") @NotNull(message = "品牌编号不能为空") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java deleted file mode 100644 index 12408ac28..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.category; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; -import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; -import cn.iocoder.yudao.module.product.service.category.CategoryService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.Comparator; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; - -@Api(tags = "管理后台 - 商品分类") -@RestController -@RequestMapping("/product/category") -@Validated -public class CategoryController { - - @Resource - private CategoryService categoryService; - - @PostMapping("/create") - @ApiOperation("创建商品分类") - @PreAuthorize("@ss.hasPermission('product:category:create')") - public CommonResult createCategory(@Valid @RequestBody CategoryCreateReqVO createReqVO) { - return success(categoryService.createCategory(createReqVO)); - } - - @PutMapping("/update") - @ApiOperation("更新商品分类") - @PreAuthorize("@ss.hasPermission('product:category:update')") - public CommonResult updateCategory(@Valid @RequestBody CategoryUpdateReqVO updateReqVO) { - categoryService.updateCategory(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @ApiOperation("删除商品分类") - @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('product:category:delete')") - public CommonResult deleteCategory(@RequestParam("id") Long id) { - categoryService.deleteCategory(id); - return success(true); - } - - @GetMapping("/get") - @ApiOperation("获得商品分类") - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('product:category:query')") - public CommonResult getCategory(@RequestParam("id") Long id) { - CategoryDO category = categoryService.getCategory(id); - return success(CategoryConvert.INSTANCE.convert(category)); - } - - // TODO @JeromeSoar:这应该是个 app 的接口,提供商品分类的树结构。这个调整下,后端只返回列表,前端构建 tree。注意,不需要返回创建时间、是否开启等无关字段。 - // TODO @YunaiV: 这个是在管理端展示了一个类似菜单的分类树列表, treeListReqVO 只是查询参数的封装命名,返给前端的是列表数据。PS: 这里 /page 接口没有使用到。 - @GetMapping("/listByQuery") - @ApiOperation("获得商品分类列表") - @PreAuthorize("@ss.hasPermission('product:category:query')") - public CommonResult> listByQuery(@Valid CategoryTreeListReqVO treeListReqVO) { - List list = categoryService.getCategoryTreeList(treeListReqVO); - list.sort(Comparator.comparing(CategoryDO::getSort)); - return success(CategoryConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/export-excel") - @ApiOperation("导出商品分类 Excel") - @PreAuthorize("@ss.hasPermission('product:category:export')") - @OperateLog(type = EXPORT) - public void exportCategoryExcel(@Valid CategoryExportReqVO exportReqVO, - HttpServletResponse response) throws IOException { - List list = categoryService.getCategoryList(exportReqVO); - // 导出 Excel - List datas = CategoryConvert.INSTANCE.convertList02(list); - ExcelUtils.write(response, "商品分类.xls", "数据", CategoryExcelVO.class, datas); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/ProductCategoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/ProductCategoryController.java new file mode 100644 index 000000000..4144e6a57 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/ProductCategoryController.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.product.controller.admin.category; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryRespVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryUpdateReqVO; +import cn.iocoder.yudao.module.product.convert.category.ProductCategoryConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 商品分类") +@RestController +@RequestMapping("/product/category") +@Validated +public class ProductCategoryController { + + @Resource + private ProductCategoryService categoryService; + + @PostMapping("/create") + @ApiOperation("创建商品分类") + @PreAuthorize("@ss.hasPermission('product:category:create')") + public CommonResult createCategory(@Valid @RequestBody ProductCategoryCreateReqVO createReqVO) { + return success(categoryService.createCategory(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新商品分类") + @PreAuthorize("@ss.hasPermission('product:category:update')") + public CommonResult updateCategory(@Valid @RequestBody ProductCategoryUpdateReqVO updateReqVO) { + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除商品分类") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:category:delete')") + public CommonResult deleteCategory(@RequestParam("id") Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得商品分类") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:category:query')") + public CommonResult getCategory(@RequestParam("id") Long id) { + ProductCategoryDO category = categoryService.getCategory(id); + return success(ProductCategoryConvert.INSTANCE.convert(category)); + } + + @GetMapping("/list") + @ApiOperation("获得商品分类列表") + @PreAuthorize("@ss.hasPermission('product:category:query')") + public CommonResult> getCategoryList(@Valid ProductCategoryListReqVO treeListReqVO) { + List list = categoryService.getEnableCategoryList(treeListReqVO); + list.sort(Comparator.comparing(ProductCategoryDO::getSort)); + return success(ProductCategoryConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExcelVO.java deleted file mode 100644 index f8e36ee8c..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExcelVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.category.vo; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import com.alibaba.excel.annotation.ExcelProperty; -import lombok.Data; - -import java.util.Date; - - -/** - * 商品分类 Excel VO - * - * @author 芋道源码 - */ -@Data -public class CategoryExcelVO { - - @ExcelProperty("分类编号") - private Long id; - - @ExcelProperty("父分类编号") - private Long parentId; - - @ExcelProperty("分类名称") - private String name; - - @ExcelProperty("分类图标") - private String icon; - - @ExcelProperty("分类图片") - private String bannerUrl; - - @ExcelProperty("分类排序") - private Integer sort; - - @ExcelProperty("分类描述") - private String description; - - @ExcelProperty(value = "开启状态", converter = DictConvert.class) - @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中 - private Integer status; - - @ExcelProperty("创建时间") - private Date createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExportReqVO.java deleted file mode 100644 index c119fb2dc..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExportReqVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.category.vo; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.util.Date; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 商品分类 Excel 导出 Request VO", description = "参数和 CategoryPageReqVO 是一致的") -@Data -public class CategoryExportReqVO { - - @ApiModelProperty(value = "分类名称", example = "办公文具") - private String name; - - @ApiModelProperty(value = "开启状态", example = "0") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryTreeListReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryTreeListReqVO.java deleted file mode 100644 index 214349ee5..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryTreeListReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.category.vo; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.util.Date; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Data -@ApiModel(value = "管理后台 - 商品分类列表查询 Request VO", description = "参数和 CategoryPageReqVO 是一致的") -public class CategoryTreeListReqVO extends CategoryExportReqVO { - - @ApiModelProperty(value = "分类名称", example = "办公文具") - private String name; - - @ApiModelProperty(value = "开启状态", example = "0") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryBaseVO.java similarity index 87% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryBaseVO.java index 598e093eb..9b9c77a08 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryBaseVO.java @@ -11,7 +11,7 @@ import javax.validation.constraints.NotNull; * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 */ @Data -public class CategoryBaseVO { +public class ProductCategoryBaseVO { @ApiModelProperty(value = "父分类编号", required = true, example = "1") @NotNull(message = "父分类编号不能为空") @@ -21,13 +21,9 @@ public class CategoryBaseVO { @NotBlank(message = "分类名称不能为空") private String name; - @ApiModelProperty(value = "分类图标") - @NotBlank(message = "分类图标不能为空") - private String icon; - @ApiModelProperty(value = "分类图片", required = true) @NotBlank(message = "分类图片不能为空") - private String bannerUrl; + private String picUrl; @ApiModelProperty(value = "分类排序", required = true, example = "1") private Integer sort; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryCreateReqVO.java similarity index 68% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryCreateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryCreateReqVO.java index ce583f08b..d35e1ad3c 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryCreateReqVO.java @@ -1,14 +1,12 @@ package cn.iocoder.yudao.module.product.controller.admin.category.vo; import lombok.*; -import java.util.*; import io.swagger.annotations.*; -import javax.validation.constraints.*; @ApiModel("管理后台 - 商品分类创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class CategoryCreateReqVO extends CategoryBaseVO { +public class ProductCategoryCreateReqVO extends ProductCategoryBaseVO { } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java new file mode 100644 index 000000000..a487c16a4 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.product.controller.admin.category.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel(value = "管理后台 - 商品分类列表查询 Request VO") +@Data +public class ProductCategoryListReqVO { + + @ApiModelProperty(value = "分类名称", example = "办公文具") + private String name; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryRespVO.java similarity index 87% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryRespVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryRespVO.java index e7d0b2238..9c3d1b458 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryRespVO.java @@ -8,7 +8,7 @@ import io.swagger.annotations.*; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class CategoryRespVO extends CategoryBaseVO { +public class ProductCategoryRespVO extends ProductCategoryBaseVO { @ApiModelProperty(value = "分类编号", required = true, example = "2") private Long id; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryUpdateReqVO.java similarity index 85% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryUpdateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryUpdateReqVO.java index 13ee83c1e..b0c8fe57d 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryUpdateReqVO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.product.controller.admin.category.vo; import lombok.*; -import java.util.*; import io.swagger.annotations.*; import javax.validation.constraints.*; @@ -9,7 +8,7 @@ import javax.validation.constraints.*; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class CategoryUpdateReqVO extends CategoryBaseVO { +public class ProductCategoryUpdateReqVO extends ProductCategoryBaseVO { @ApiModelProperty(value = "分类编号", required = true, example = "2") @NotNull(message = "分类编号不能为空") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java index f1d31acab..7401ec744 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java @@ -1,29 +1,23 @@ package cn.iocoder.yudao.module.product.controller.admin.property; -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.annotations.*; - -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; -import cn.iocoder.yudao.module.product.convert.property.ProductPropertyConvert; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyUpdateReqVO; import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Api(tags = "管理后台 - 规格名称") @RestController @@ -62,36 +56,15 @@ public class ProductPropertyController { @ApiOperation("获得规格名称") @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @PreAuthorize("@ss.hasPermission('product:property:query')") - public CommonResult getProperty(@RequestParam("id") Long id) { + public CommonResult getProperty(@RequestParam("id") Long id) { return success(productPropertyService.getPropertyResp(id)); } - @GetMapping("/list") - @ApiOperation("获得规格名称列表") - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) - @PreAuthorize("@ss.hasPermission('product:property:query')") - public CommonResult> getPropertyList(@RequestParam("ids") Collection ids) { - List list = productPropertyService.getPropertyList(ids); - return success(ProductPropertyConvert.INSTANCE.convertList(list)); - } - @GetMapping("/page") @ApiOperation("获得规格名称分页") @PreAuthorize("@ss.hasPermission('product:property:query')") - public CommonResult> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) { + public CommonResult> getPropertyPage(@Valid ProductPropertyPageReqVO pageVO) { return success(productPropertyService.getPropertyListPage(pageVO)); } - @GetMapping("/export-excel") - @ApiOperation("导出规格名称 Excel") - @PreAuthorize("@ss.hasPermission('product:property:export')") - @OperateLog(type = EXPORT) - public void exportPropertyExcel(@Valid ProductPropertyExportReqVO exportReqVO, - HttpServletResponse response) throws IOException { - List list = productPropertyService.getPropertyList(exportReqVO); - // 导出 Excel - List datas = ProductPropertyConvert.INSTANCE.convertList02(list); - ExcelUtils.write(response, "规格名称.xls", "数据", ProductPropertyExcelVO.class, datas); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyBaseVO.java deleted file mode 100644 index 4d38763f9..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyBaseVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -/** -* 规格名称 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ -@Data -public class ProductPropertyBaseVO { - - @ApiModelProperty(value = "规格名称") - private String name; - - @ApiModelProperty(value = "状态: 0 开启 ,1 禁用") - private Integer status; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExcelVO.java deleted file mode 100644 index c935c1001..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExcelVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; - -import com.alibaba.excel.annotation.ExcelProperty; - -/** - * 规格名称 Excel VO - * - * @author 芋道源码 - */ -@Data -public class ProductPropertyExcelVO { - - @ExcelProperty("主键") - private Long id; - - @ExcelProperty("规格名称") - private String name; - - @ExcelProperty("状态: 0 开启 ,1 禁用") - private Integer status; - - @ExcelProperty("创建时间") - private Date createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExportReqVO.java deleted file mode 100644 index 119c76e03..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExportReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 规格名称 Excel 导出 Request VO", description = "参数和 PropertyPageReqVO 是一致的") -@Data -public class ProductPropertyExportReqVO { - - @ApiModelProperty(value = "规格名称") - private String name; - - @ApiModelProperty(value = "状态: 0 开启 ,1 禁用") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyRespVO.java deleted file mode 100644 index 978f26308..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyRespVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; - -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; - -@ApiModel("管理后台 - 规格名称 Response VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ProductPropertyRespVO extends ProductPropertyBaseVO { - - @ApiModelProperty(value = "主键", required = true) - private Long id; - - @ApiModelProperty(value = "创建时间") - private Date createTime; - - @ApiModelProperty(value = "属性值") - private List propertyValueList; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java new file mode 100644 index 000000000..9bdc70bd7 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyViewRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +import java.util.List; + +/** + * @Description: ProductPropertyViewRespVO + * @Author: franky + * @CreateDate: 2022/7/5 21:29 + * @Version: 1.0.0 + */ +@ApiModel("管理后台 - 规格名称详情展示 Request VO") +@Data +@ToString(callSuper = true) +public class ProductPropertyViewRespVO { + + @ApiModelProperty(value = "规格名称id", example = "1") + public Long propertyId; + + @ApiModelProperty(value = "规格名称", example = "内存") + public String name; + + @ApiModelProperty(value = "规格属性值集合", example = "[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]") + public List propertyValues; + + @Data + @ApiModel(value = "规格属性值元组") + public static class Tuple2 { + private final long id; + private final String name; + + public Tuple2(Long id, String name) { + this.id = id; + this.name = name; + } + + } + + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java new file mode 100644 index 000000000..7684d4ea7 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyAndValueRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; + +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; +import java.util.List; + +@ApiModel("管理后台 - 规格 + 规格值 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyAndValueRespVO extends ProductPropertyBaseVO { + + @ApiModelProperty(value = "规格的编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 规格值的集合 + */ + private List values; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java new file mode 100644 index 000000000..c900a727b --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyBaseVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** +* 规格名称 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class ProductPropertyBaseVO { + + @ApiModelProperty(value = "规格名称", required = true, example = "颜色") + @NotEmpty(message = "规格名称不能为空") + private String name; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + @NotEmpty(message = "状态不能为空") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java similarity index 71% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyCreateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java index 54d72da8a..c0e6b9da2 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyCreateReqVO.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; import lombok.*; import io.swagger.annotations.*; @@ -13,6 +13,7 @@ import java.util.List; @ToString(callSuper = true) public class ProductPropertyCreateReqVO extends ProductPropertyBaseVO { + // TODO @Luowenfeng:规格值的 CRUD 可以单独;前端 + 后端,改成类似字典类型、字典数据的这种交互;在加一个 ProductPropertyValueController @ApiModelProperty(value = "属性值") @NotNull(message = "属性值不能为空") List propertyValueList; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java similarity index 64% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyPageReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java index 360a0a5e1..1902fb205 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyPageReqVO.java @@ -1,11 +1,15 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; +import java.util.Date; + import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @ApiModel("管理后台 - 规格名称分页 Request VO") @@ -14,10 +18,10 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @ToString(callSuper = true) public class ProductPropertyPageReqVO extends PageParam { - @ApiModelProperty(value = "规格名称") + @ApiModelProperty(value = "规格名称", example = "颜色") private String name; - @ApiModelProperty(value = "状态: 0 开启 ,1 禁用") + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") private Integer status; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java similarity index 83% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyUpdateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java index ed190e8b0..f4b9d695a 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/property/ProductPropertyUpdateReqVO.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.product.controller.admin.property.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.property; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; import lombok.*; import io.swagger.annotations.*; import javax.validation.constraints.*; @@ -12,7 +12,7 @@ import java.util.List; @ToString(callSuper = true) public class ProductPropertyUpdateReqVO extends ProductPropertyBaseVO { - @ApiModelProperty(value = "主键", required = true) + @ApiModelProperty(value = "主键", required = true, example = "1") @NotNull(message = "主键不能为空") private Long id; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java new file mode 100644 index 000000000..1e0708009 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueBaseVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** +* 规格值 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class ProductPropertyValueBaseVO { + + @ApiModelProperty(value = "规格编号", required = true, example = "1024") + @NotNull(message = "规格编号不能为空") + private Long propertyId; + + @ApiModelProperty(value = "规格值名字", required = true, example = "红色") + @NotEmpty(message = "规格值名字不能为空") + private String name; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + @NotEmpty(message = "状态不能为空") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java similarity index 96% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueCreateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java index 23ea0690c..f7237f9cd 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueCreateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value; import lombok.*; import io.swagger.annotations.*; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java similarity index 57% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueRespVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java index 25fa25f02..17eedaae9 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueRespVO.java @@ -1,8 +1,12 @@ -package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; @ApiModel("管理后台 - 规格值 Response VO") @Data @@ -10,7 +14,7 @@ import io.swagger.annotations.*; @ToString(callSuper = true) public class ProductPropertyValueRespVO extends ProductPropertyValueBaseVO { - @ApiModelProperty(value = "主键", required = true) + @ApiModelProperty(value = "主键", required = true, example = "10") private Long id; @ApiModelProperty(value = "创建时间") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java similarity index 83% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java index 894d6f6eb..4a1bc5778 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/value/ProductPropertyValueUpdateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; +package cn.iocoder.yudao.module.product.controller.admin.property.vo.value; import lombok.*; import io.swagger.annotations.*; @@ -10,7 +10,7 @@ import javax.validation.constraints.*; @ToString(callSuper = true) public class ProductPropertyValueUpdateReqVO extends ProductPropertyValueBaseVO { - @ApiModelProperty(value = "主键", required = true) + @ApiModelProperty(value = "主键", required = true, example = "1024") @NotNull(message = "主键不能为空") private Integer id; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueBaseVO.java deleted file mode 100644 index ed600a9ac..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueBaseVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -/** -* 规格值 Base VO,提供给添加、修改、详细的子 VO 使用 -* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 -*/ -@Data -public class ProductPropertyValueBaseVO { - - @ApiModelProperty(value = "规格键id") - private Long propertyId; - - @ApiModelProperty(value = "规格值名字") - private String name; - - @ApiModelProperty(value = "状态: 1 开启 ,2 禁用") - private Integer status; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java index 132d68d94..2081e7da8 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java @@ -1,29 +1,9 @@ package cn.iocoder.yudao.module.product.controller.admin.sku; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*; -import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; -import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.io.IOException; -import java.util.Collection; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @Api(tags = "管理后台 - 商品 sku") @RestController @@ -31,69 +11,4 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E @Validated public class ProductSkuController { - @Resource - private ProductSkuService ProductSkuService; - - @PostMapping("/create") - @ApiOperation("创建商品sku") - @PreAuthorize("@ss.hasPermission('product:sku:create')") - public CommonResult createSku(@Valid @RequestBody ProductSkuCreateReqVO createReqVO) { - return success(ProductSkuService.createSku(createReqVO)); - } - - @PutMapping("/update") - @ApiOperation("更新商品sku") - @PreAuthorize("@ss.hasPermission('product:sku:update')") - public CommonResult updateSku(@Valid @RequestBody ProductSkuUpdateReqVO updateReqVO) { - ProductSkuService.updateSku(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @ApiOperation("删除商品sku") - @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('product:sku:delete')") - public CommonResult deleteSku(@RequestParam("id") Long id) { - ProductSkuService.deleteSku(id); - return success(true); - } - - @GetMapping("/get") - @ApiOperation("获得商品sku") - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) - @PreAuthorize("@ss.hasPermission('product:sku:query')") - public CommonResult getSku(@RequestParam("id") Long id) { - ProductSkuDO sku = ProductSkuService.getSku(id); - return success(ProductSkuConvert.INSTANCE.convert(sku)); - } - - @GetMapping("/list") - @ApiOperation("获得商品sku列表") - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) - @PreAuthorize("@ss.hasPermission('product:sku:query')") - public CommonResult> getSkuList(@RequestParam("ids") Collection ids) { - List list = ProductSkuService.getSkuList(ids); - return success(ProductSkuConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @ApiOperation("获得商品sku分页") - @PreAuthorize("@ss.hasPermission('product:sku:query')") - public CommonResult> getSkuPage(@Valid ProductSkuPageReqVO pageVO) { - PageResult pageResult = ProductSkuService.getSkuPage(pageVO); - return success(ProductSkuConvert.INSTANCE.convertPage(pageResult)); - } - - @GetMapping("/export-excel") - @ApiOperation("导出商品sku Excel") - @PreAuthorize("@ss.hasPermission('product:sku:export')") - @OperateLog(type = EXPORT) - public void exportSkuExcel(@Valid ProductSkuExportReqVO exportReqVO, - HttpServletResponse response) throws IOException { - List list = ProductSkuService.getSkuList(exportReqVO); - // 导出 Excel - List datas = ProductSkuConvert.INSTANCE.convertList02(list); - ExcelUtils.write(response, "商品sku.xls", "数据", ProductSkuExcelVO.class, datas); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java index cefab9a47..43f67ee53 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java @@ -1,58 +1,72 @@ package cn.iocoder.yudao.module.product.controller.admin.sku.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; /** -* 商品sku Base VO,提供给添加、修改、详细的子 VO 使用 +* 商品 SKU Base VO,提供给添加、修改、详细的子 VO 使用 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 */ @Data public class ProductSkuBaseVO { - // TODO @franky:example 要写哈; + @ApiModelProperty(value = "商品 SKU 名字", required = true, example = "芋道") + @NotEmpty(message = "商品 SKU 名字不能为空") + private String name; - @ApiModelProperty(value = "spu编号") - private Long spuId; - - // TODO @franky:类似这种字段,有额外说明的。可以写成; @ApiModelProperty(value = "规格值数组", required = true, notes = "json格式, [{propertyId: , valueId: }, {propertyId: , valueId: }]") - - @ApiModelProperty(value = "规格值数组-json格式, [{propertyId: , valueId: }, {propertyId: , valueId: }]", required = true) - @NotNull(message = "规格值数组-json格式, [{propertyId: , valueId: }, {propertyId: , valueId: }]不能为空") - private List properties; - - @ApiModelProperty(value = "销售价格,单位:分", required = true) + @ApiModelProperty(value = "销售价格,单位:分", required = true, example = "1024", notes = "单位:分") @NotNull(message = "销售价格,单位:分不能为空") private Integer price; - @ApiModelProperty(value = "原价, 单位: 分", required = true) - @NotNull(message = "原价, 单位: 分不能为空") - private Integer originalPrice; + @ApiModelProperty(value = "市场价", example = "1024", notes = "单位:分") + private Integer marketPrice; - @ApiModelProperty(value = "成本价,单位: 分", required = true) - @NotNull(message = "成本价,单位: 分不能为空") + @ApiModelProperty(value = "成本价", example = "1024", notes = "单位:分") private Integer costPrice; - @ApiModelProperty(value = "条形码", required = true) - @NotNull(message = "条形码不能为空") + @ApiModelProperty(value = "条形码", example = "haha") private String barCode; - @ApiModelProperty(value = "图片地址", required = true) + @ApiModelProperty(value = "图片地址") @NotNull(message = "图片地址不能为空") private String picUrl; - @ApiModelProperty(value = "状态: 0-正常 1-禁用") + @ApiModelProperty(value = "SKU 状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + @NotNull(message = "SKU 状态不能为空") + @InEnum(CommonStatusEnum.class) private Integer status; - // TODO @franky 要有 swagger 注解 + @ApiModelProperty(value = "库存", required = true, example = "1") + @NotNull(message = "库存不能为空") + private Integer stock; + + @ApiModelProperty(value = "预警预存", example = "1") + private Integer warnStock; + + @ApiModelProperty(value = "商品重量", example = "1", notes = "单位:kg 千克") + private Double weight; + + @ApiModelProperty(value = "商品体积", example = "1024", notes = "单位:m^3 平米") + private Double volume; + + @ApiModel("规格值") @Data public static class Property { - @NotNull(message = "规格属性名id不能为空") + + @ApiModelProperty(value = "属性编号", required = true, example = "1") + @NotNull(message = "属性编号不能为空") private Long propertyId; - @NotNull(message = "规格属性值id不能为空") + + @ApiModelProperty(value = "属性值编号", required = true, example = "1024") + @NotNull(message = "属性值编号不能为空") private Long valueId; + } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java new file mode 100755 index 000000000..84d71d9b7 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateOrUpdateReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.product.controller.admin.sku.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@ApiModel("管理后台 - 商品 SKU 创建/更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO { + + // TODO @Luowenfeng:可以不用哈,如果基于规格匹配 + @ApiModelProperty(value = "商品 id 更新时须有", example = "1") + private Long id; + + /** + * 规格值数组 + */ + private List properties; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExcelVO.java deleted file mode 100755 index 7caf1313d..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExcelVO.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.sku.vo; - -import com.alibaba.excel.annotation.ExcelProperty; -import lombok.Data; - -import java.util.Date; -import java.util.List; - -/** - * 商品sku Excel VO - * - * @author 芋道源码 - */ -@Data -public class ProductSkuExcelVO { - - @ExcelProperty("主键") - private Long id; - - @ExcelProperty("spu编号") - private Long spuId; - - // TODO @franky:这个单元格,可能会有点展示的问题 - @ExcelProperty("规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]") - private List properties; - - @ExcelProperty("销售价格,单位:分") - private Integer price; - - @ExcelProperty("原价, 单位: 分") - private Integer originalPrice; - - @ExcelProperty("成本价,单位: 分") - private Integer costPrice; - - @ExcelProperty("条形码") - private String barCode; - - @ExcelProperty("图片地址") - private String picUrl; - - @ExcelProperty("状态: 0-正常 1-禁用") - private Integer status; - - @ExcelProperty("创建时间") - private Date createTime; - - @Data - public static class Property { - private Integer propertyId; - private Integer valueId; - } -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExportReqVO.java deleted file mode 100755 index 0bf34a67d..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExportReqVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.sku.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 商品sku Excel 导出 Request VO", description = "参数和 SkuPageReqVO 是一致的") -@Data -public class ProductSkuExportReqVO { - - @ApiModelProperty(value = "spu编号") - private Long spuId; - - @ApiModelProperty(value = "规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]") - private String properties; - - @ApiModelProperty(value = "销售价格,单位:分") - private Integer price; - - @ApiModelProperty(value = "原价, 单位: 分") - private Integer originalPrice; - - @ApiModelProperty(value = "成本价,单位: 分") - private Integer costPrice; - - @ApiModelProperty(value = "条形码") - private String barCode; - - @ApiModelProperty(value = "图片地址") - private String picUrl; - - @ApiModelProperty(value = "状态: 0-正常 1-禁用") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuPageReqVO.java deleted file mode 100755 index b5a494902..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuPageReqVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.sku.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel("管理后台 - 商品sku分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ProductSkuPageReqVO extends PageParam { - - @ApiModelProperty(value = "spu编号") - private Long spuId; - - @ApiModelProperty(value = "规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }]") - private String properties; - - @ApiModelProperty(value = "销售价格,单位:分") - private Integer price; - - @ApiModelProperty(value = "原价, 单位: 分") - private Integer originalPrice; - - @ApiModelProperty(value = "成本价,单位: 分") - private Integer costPrice; - - @ApiModelProperty(value = "条形码") - private String barCode; - - @ApiModelProperty(value = "图片地址") - private String picUrl; - - @ApiModelProperty(value = "状态: 0-正常 1-禁用") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java index 3b12ba21c..46e76cc63 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java @@ -1,8 +1,13 @@ package cn.iocoder.yudao.module.product.controller.admin.sku.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; +import java.util.List; @ApiModel("管理后台 - 商品sku Response VO") @Data @@ -10,10 +15,15 @@ import io.swagger.annotations.*; @ToString(callSuper = true) public class ProductSkuRespVO extends ProductSkuBaseVO { - @ApiModelProperty(value = "主键", required = true) + @ApiModelProperty(value = "主键", required = true, example = "1024") private Long id; @ApiModelProperty(value = "创建时间") private Date createTime; + /** + * 规格值数组 + */ + private List properties; + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuUpdateReqVO.java deleted file mode 100755 index 984976eee..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuUpdateReqVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.sku.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 商品sku更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ProductSkuUpdateReqVO extends ProductSkuBaseVO { - - @ApiModelProperty(value = "主键", required = true) - @NotNull(message = "主键不能为空") - private Long id; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java index 82c6b5df5..ddae45dc2 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java @@ -2,9 +2,10 @@ package cn.iocoder.yudao.module.product.controller.admin.spu; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; @@ -16,16 +17,13 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.IOException; import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; -@Api(tags = "管理后台 - 商品spu") +@Api(tags = "管理后台 - 商品 SPU") @RestController @RequestMapping("/product/spu") @Validated @@ -35,23 +33,22 @@ public class ProductSpuController { private ProductSpuService spuService; @PostMapping("/create") - @ApiOperation("创建商品spu") + @ApiOperation("创建商品 SPU") @PreAuthorize("@ss.hasPermission('product:spu:create')") - public CommonResult createSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { + public CommonResult createProductSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { return success(spuService.createSpu(createReqVO)); } - // TODO @franky:SpuUpdateReqVO 缺少前缀 @PutMapping("/update") - @ApiOperation("更新商品spu") + @ApiOperation("更新商品 SPU") @PreAuthorize("@ss.hasPermission('product:spu:update')") - public CommonResult updateSpu(@Valid @RequestBody SpuUpdateReqVO updateReqVO) { + public CommonResult updateSpu(@Valid @RequestBody ProductSpuUpdateReqVO updateReqVO) { spuService.updateSpu(updateReqVO); return success(true); } @DeleteMapping("/delete") - @ApiOperation("删除商品spu") + @ApiOperation("删除商品 SPU") @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) @PreAuthorize("@ss.hasPermission('product:spu:delete')") public CommonResult deleteSpu(@RequestParam("id") Long id) { @@ -60,39 +57,29 @@ public class ProductSpuController { } @GetMapping("/get") - @ApiOperation("获得商品spu") + @ApiOperation("获得商品 SPU") @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) @PreAuthorize("@ss.hasPermission('product:spu:query')") - public CommonResult getSpu(@RequestParam("id") Long id) { + public CommonResult getSpu(@RequestParam("id") Long id) { return success(spuService.getSpu(id)); } + // TODO @luowenfeng:新增 get-detail,返回 SpuDetailRespVO + @GetMapping("/list") - @ApiOperation("获得商品spu列表") + @ApiOperation("获得商品 SPU 列表") @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class) @PreAuthorize("@ss.hasPermission('product:spu:query')") - public CommonResult> getSpuList(@RequestParam("ids") Collection ids) { + public CommonResult> getSpuList(@RequestParam("ids") Collection ids) { List list = spuService.getSpuList(ids); return success(ProductSpuConvert.INSTANCE.convertList(list)); } @GetMapping("/page") - @ApiOperation("获得商品spu分页") + @ApiOperation("获得商品 SPU 分页") @PreAuthorize("@ss.hasPermission('product:spu:query')") - public CommonResult> getSpuPage(@Valid SpuPageReqVO pageVO) { + public CommonResult> getSpuPage(@Valid ProductSpuPageReqVO pageVO) { return success(spuService.getSpuPage(pageVO)); } - @GetMapping("/export-excel") - @ApiOperation("导出商品spu Excel") - @PreAuthorize("@ss.hasPermission('product:spu:export')") - @OperateLog(type = EXPORT) - public void exportSpuExcel(@Valid SpuExportReqVO exportReqVO, - HttpServletResponse response) throws IOException { - List list = spuService.getSpuList(exportReqVO); - // 导出 Excel - List datas = ProductSpuConvert.INSTANCE.convertList02(list); - ExcelUtils.write(response, "商品spu.xls", "数据", SpuExcelVO.class, datas); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java index 65a5f2a0e..ec58bab76 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java @@ -1,50 +1,84 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; /** -* 商品spu Base VO,提供给添加、修改、详细的子 VO 使用 +* 商品 SPU Base VO,提供给添加、修改、详细的子 VO 使用 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 */ @Data public class ProductSpuBaseVO { - @ApiModelProperty(value = "商品名称") + @ApiModelProperty(value = "商品名称", required = true, example = "芋道") + @NotEmpty(message = "商品名称不能为空") private String name; - @ApiModelProperty(value = "卖点", required = true) - @NotNull(message = "卖点不能为空") + @ApiModelProperty(value = "商品编码", example = "yudaoyuanma") + private String code; + + @ApiModelProperty(value = "商品卖点", example = "好吃!") private String sellPoint; - @ApiModelProperty(value = "描述", required = true) - @NotNull(message = "描述不能为空") + @ApiModelProperty(value = "商品详情", required = true, example = "我是商品描述") + @NotNull(message = "商品详情不能为空") private String description; - @ApiModelProperty(value = "分类id", required = true) - @NotNull(message = "分类id不能为空") + @ApiModelProperty(value = "商品分类编号", required = true, example = "1") + @NotNull(message = "商品分类编号不能为空") private Long categoryId; - @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true) - @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空") + @ApiModelProperty(value = "商品品牌编号", example = "1") + private Long brandId; + + @ApiModelProperty(value = "商品图片的数组", required = true) + @NotNull(message = "商品图片的数组不能为空") private List picUrls; - @ApiModelProperty(value = "排序字段", required = true) - @NotNull(message = "排序字段不能为空") + @ApiModelProperty(value = "商品视频", required = true) + private String videoUrl; + + @ApiModelProperty(value = "排序字段", required = true, example = "1") private Integer sort; - @ApiModelProperty(value = "点赞初始人数") - private Integer likeCount; + @ApiModelProperty(value = "商品状态", required = true, example = "1", notes = "参见 ProductSpuStatusEnum 枚举类") + @NotNull(message = "商品状态不能为空") + @InEnum(ProductSpuStatusEnum.class) + private Integer status; - @ApiModelProperty(value = "价格 单位使用:分") - private Integer price; + // ========== SKU 相关字段 ========= - @ApiModelProperty(value = "库存数量") - private Integer quantity; + @ApiModelProperty(value = "规格类型", required = true, example = "1", notes = "参见 ProductSpuSpecTypeEnum 枚举类") + @NotNull(message = "规格类型不能为空") + @InEnum(ProductSpuSpecTypeEnum.class) + private Integer specType; - @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") - private Boolean status; + @ApiModelProperty(value = "是否展示库存", required = true, example = "true") + @NotNull(message = "是否展示库存不能为空") + private Boolean showStock; + @ApiModelProperty(value = "库存", required = true, example = "true") + private Integer totalStock; + + @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024") + private Integer minPrice; + + @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024") + private Integer maxPrice; + + // ========== 统计相关字段 ========= + + @ApiModelProperty(value = "虚拟销量", required = true, example = "1024") + @NotNull(message = "虚拟销量不能为空") + private Integer virtualSalesCount; + + @ApiModelProperty(value = "点击量", example = "1024") + private Integer clickCount; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java index 6281e20d5..87b0dd0b6 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -10,14 +9,16 @@ import lombok.ToString; import javax.validation.Valid; import java.util.List; -@ApiModel("管理后台 - 商品spu创建 Request VO") +@ApiModel("管理后台 - 商品 SPU 创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class ProductSpuCreateReqVO extends ProductSpuBaseVO { - @ApiModelProperty(value = "sku组合") + /** + * SKU 数组 + */ @Valid - List skus; + private List skus; } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java new file mode 100644 index 000000000..dafb0a680 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; +import java.util.List; + +@ApiModel(value = "管理后台 - 商品 SPU 详细 Response VO", description = "包括关联的 SKU 等信息") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSpuDetailRespVO extends ProductSpuBaseVO { + + @ApiModelProperty(value = "主键", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + /** + * SKU 数组 + */ + private List skus; + + @ApiModel(value = "管理后台 - 商品 SKU 详细 Response VO") + @Data + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class Sku extends ProductSkuBaseVO { + + /** + * 规格的数组 + */ + private List properties; + + } + + @ApiModel(value = "管理后台 - 商品规格的详细 Response VO") + @Data + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class Property extends ProductSkuBaseVO.Property { + + @ApiModelProperty(value = "规格的名字", required = true, example = "颜色") + private String propertyName; + + @ApiModelProperty(value = "规格值的名字", required = true, example = "蓝色") + private String valueName; + + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java similarity index 91% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuPageReqVO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java index 12eff36c7..36a37541b 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java @@ -8,11 +8,11 @@ import org.springframework.format.annotation.DateTimeFormat; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@ApiModel("管理后台 - 商品spu分页 Request VO") +@ApiModel("管理后台 - 商品 SPU 分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class SpuPageReqVO extends PageParam { +public class ProductSpuPageReqVO extends PageParam { @ApiModelProperty(value = "商品名称") private String name; @@ -42,7 +42,7 @@ public class SpuPageReqVO extends PageParam { private Integer quantity; @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") - private Boolean status; + private Integer status; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "创建时间") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java new file mode 100755 index 000000000..9db319fdc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; +import java.util.List; + +// TODO @Luowenfeng:这个类只返回 SPU 相关的信息,删除 skus、categoryIds、productPropertyViews;明细使用 SpuDetailRespVO 替代 +@ApiModel("管理后台 - 商品 SPU Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSpuRespVO extends ProductSpuBaseVO { + + @ApiModelProperty(value = "主键", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + /** + * SKU 数组 + */ + @ApiModelProperty(value = "sku 数组", example = "[{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":10}],\"price\":12,\"originalPrice\":32,\"costPrice\":22,\"barCode\":\"765670123123\",\"picUrl\":\"http://test.yudao.iocoder.cn/72938088f1ca8438837c3b51394aea43.jpg\",\"status\":0,\"id\":7,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":15},{\"propertyId\":2,\"valueId\":11}],\"price\":13,\"originalPrice\":33,\"costPrice\":23,\"barCode\":\"888788770999\",\"picUrl\":\"http://test.yudao.iocoder.cn/6b902c700e5d32e862b6fd9af2e1c0e4.jpg\",\"status\":0,\"id\":8,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":10}],\"price\":14,\"originalPrice\":34,\"costPrice\":24,\"barCode\":\"9999981212\",\"picUrl\":\"http://test.yudao.iocoder.cn/eddf3c79b1917160d94d05244e1f47da.jpg\",\"status\":0,\"id\":9,\"createTime\":1656683270000},{\"spuId\":4,\"properties\":[{\"propertyId\":3,\"valueId\":16},{\"propertyId\":2,\"valueId\":11}],\"price\":15,\"originalPrice\":35,\"costPrice\":25,\"barCode\":\"4444121212\",\"picUrl\":\"http://test.yudao.iocoder.cn/88ac3eb068ea9cfac4726879b2776ccf.jpg\",\"status\":0,\"id\":10,\"createTime\":1656683270000}]") + private List skus; + + @ApiModelProperty(value = "分类id数组,一直递归到一级父节点", example = "[1,2,4]") + private List categoryIds; + + // TODO @芋艿:再琢磨下 这个 VO 类,其实变成 SpuRespVO 内嵌的 VO 类会更好一点;然后把 SpuRespVO 改成 SpuDetailSpuVO + + @ApiModelProperty(value = "规格属性修改和详情展示组合", example = "[{\"propertyId\":2,\"name\":\"内存\",\"propertyValues\":[{\"v1\":11,\"v2\":\"64G\"},{\"v1\":10,\"v2\":\"32G\"}]},{\"propertyId\":3,\"name\":\"尺寸\",\"propertyValues\":[{\"v1\":16,\"v2\":\"6.1\"},{\"v1\":15,\"v2\":\"5.7\"}]}]") + private List productPropertyViews; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java new file mode 100755 index 000000000..378a98dc6 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 商品 SPU 更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSpuUpdateReqVO extends ProductSpuBaseVO { + + @ApiModelProperty(value = "商品编号", required = true, example = "1") + @NotNull(message = "商品编号不能为空") + private Long id; + + /** + * SKU 数组 + */ + @Valid + private List skus; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExcelVO.java deleted file mode 100755 index 9e90acd3d..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExcelVO.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.spu.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; - -import com.alibaba.excel.annotation.ExcelProperty; - -/** - * 商品spu Excel VO - * - * @author 芋道源码 - */ -@Data -public class SpuExcelVO { - - @ExcelProperty("主键") - private Integer id; - - @ExcelProperty("商品名称") - private String name; - - @ExcelProperty("卖点") - private String sellPoint; - - @ExcelProperty("描述") - private String description; - - @ExcelProperty("分类id") - private Long categoryId; - - @ExcelProperty("商品主图地址,* 数组,以逗号分隔,最多上传15张") - private List picUrls; - - @ExcelProperty("排序字段") - private Integer sort; - - @ExcelProperty("点赞初始人数") - private Integer likeCount; - - @ExcelProperty("价格 单位使用:分") - private Integer price; - - @ExcelProperty("库存数量") - private Integer quantity; - - @ExcelProperty("上下架状态: 0 上架(开启) 1 下架(禁用)") - private Boolean status; - - @ExcelProperty("创建时间") - private Date createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExportReqVO.java deleted file mode 100755 index 29a3086a2..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExportReqVO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.spu.vo; - -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@ApiModel(value = "管理后台 - 商品spu Excel 导出 Request VO", description = "参数和 SpuPageReqVO 是一致的") -@Data -public class SpuExportReqVO { - - @ApiModelProperty(value = "商品名称") - private String name; - - @ApiModelProperty(value = "卖点") - private String sellPoint; - - @ApiModelProperty(value = "描述") - private String description; - - @ApiModelProperty(value = "分类id") - private Long categoryId; - - @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张") - private List picUrls; - - @ApiModelProperty(value = "排序字段") - private Integer sort; - - @ApiModelProperty(value = "点赞初始人数") - private Integer likeCount; - - @ApiModelProperty(value = "价格 单位使用:分") - private Integer price; - - @ApiModelProperty(value = "库存数量") - private Integer quantity; - - @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") - private Boolean status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @ApiModelProperty(value = "创建时间") - private Date[] createTime; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuRespVO.java deleted file mode 100755 index 90387f90a..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuRespVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.spu.vo; - -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -@ApiModel("管理后台 - 商品spu Response VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SpuRespVO extends ProductSpuBaseVO { - - // TODO @franky:注解要完整 - - @ApiModelProperty(value = "主键", required = true) - private Long id; - - @ApiModelProperty(value = "创建时间") - private Date createTime; - - /** - * SKU 数组 - */ - List skus; - - @ApiModelProperty(value = "分类id数组,一直递归到一级父节点", example = "[1,2,4]") - LinkedList categoryIds; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuUpdateReqVO.java deleted file mode 100755 index 9e4092235..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuUpdateReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.product.controller.admin.spu.vo; - -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; - -import javax.validation.Valid; -import javax.validation.constraints.*; - -@ApiModel("管理后台 - 商品spu更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SpuUpdateReqVO extends ProductSpuBaseVO { - - @ApiModelProperty(value = "主键", required = true) - @NotNull(message = "主键不能为空") - private Long id; - - @ApiModelProperty(value = "sku组合") - @Valid - List skus; - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java new file mode 100644 index 000000000..fec882c48 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/AppCategoryController.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.controller.app.category; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryRespVO; +import cn.iocoder.yudao.module.product.convert.category.ProductCategoryConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "用户 APP - 商品分类") +@RestController +@RequestMapping("/product/category") +@Validated +public class AppCategoryController { + + @Resource + private ProductCategoryService categoryService; + + @GetMapping("/list") + @ApiOperation("获得商品分类列表") + public CommonResult> getProductCategoryList() { + List list = categoryService.getEnableCategoryList(); + list.sort(Comparator.comparing(ProductCategoryDO::getSort)); + return success(ProductCategoryConvert.INSTANCE.convertList03(list)); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryRespVO.java new file mode 100644 index 000000000..e46a50cd3 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/category/vo/AppCategoryRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.product.controller.app.category.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +@ApiModel(value = "用户 APP - 商品分类 Response VO") +public class AppCategoryRespVO { + + @ApiModelProperty(value = "分类编号", required = true, example = "2") + private Long id; + + @ApiModelProperty(value = "父分类编号", required = true, example = "1") + @NotNull(message = "父分类编号不能为空") + private Long parentId; + + @ApiModelProperty(value = "分类名称", required = true, example = "办公文具") + @NotBlank(message = "分类名称不能为空") + private String name; + + @ApiModelProperty(value = "分类图片", required = true) + @NotBlank(message = "分类图片不能为空") + private String picUrl; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java new file mode 100644 index 000000000..7e0f912b4 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.product.controller.app.spu; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuRespVO; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "用户 APP - 商品spu") +@RestController +@RequestMapping("/product/spu") +@Validated +public class AppProductSpuController { + + @Resource + private ProductSpuService spuService; + + @GetMapping("/page") + @ApiOperation("获得商品spu分页") + public CommonResult> getSpuPage(@Valid AppSpuPageReqVO pageVO) { + return success(spuService.getSpuPage(pageVO)); + } + + @GetMapping("/") + @ApiOperation("获取商品 - 通过商品id") + public CommonResult getSpu(@RequestParam("spuId") Long spuId) { + AppSpuRespVO appSpuRespVO = BeanUtil.toBean(spuService.getSpu(spuId), AppSpuRespVO.class); + return success(appSpuRespVO); + } +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java new file mode 100644 index 000000000..38ed4975e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.product.controller.app.spu.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("App - 商品spu分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppSpuPageReqVO extends PageParam { + + @ApiModelProperty(value = "分类id") + private Long categoryId; +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java new file mode 100644 index 000000000..c0a0f0eb8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuPageRespVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.product.controller.app.spu.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("App - 商品spu分页 Request VO") +@Data +public class AppSpuPageRespVO { + + @ApiModelProperty(value = "主键", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "商品名称") + private String name; + + @ApiModelProperty(value = "卖点", required = true) + @NotNull(message = "卖点不能为空") + private String sellPoint; + + @ApiModelProperty(value = "描述", required = true) + @NotNull(message = "描述不能为空") + private String description; + + @ApiModelProperty(value = "分类id", required = true) + @NotNull(message = "分类id不能为空") + private Long categoryId; + + @ApiModelProperty(value = "商品主图地址,* 数组,以逗号分隔,最多上传15张", required = true) + @NotNull(message = "商品主图地址,* 数组,以逗号分隔,最多上传15张不能为空") + private List picUrls; + + @ApiModelProperty(value = "排序字段", required = true) + @NotNull(message = "排序字段不能为空") + private Integer sort; + + @ApiModelProperty(value = "点赞初始人数") + private Integer likeCount; + + @ApiModelProperty(value = "价格 单位使用:分") + private Integer price; + + @ApiModelProperty(value = "库存数量") + private Integer quantity; + + @ApiModelProperty(value = "上下架状态: 0 上架(开启) 1 下架(禁用)") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java new file mode 100644 index 000000000..b45987370 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppSpuRespVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.product.controller.app.spu.vo; + +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author LuoWenFeng + */ +@ApiModel("App - 商品spu Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class AppSpuRespVO extends ProductSpuRespVO { + + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/BrandConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/BrandConvert.java deleted file mode 100644 index 316515603..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/BrandConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.product.convert.brand; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; - -/** - * 品牌 Convert - * - * @author 芋道源码 - */ -@Mapper -public interface BrandConvert { - - BrandConvert INSTANCE = Mappers.getMapper(BrandConvert.class); - - BrandDO convert(BrandCreateReqVO bean); - - BrandDO convert(BrandUpdateReqVO bean); - - BrandRespVO convert(BrandDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - List convertList02(List list); - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java new file mode 100644 index 000000000..a318e9128 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/ProductBrandConvert.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.product.convert.brand; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandRespVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 品牌 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface ProductBrandConvert { + + ProductBrandConvert INSTANCE = Mappers.getMapper(ProductBrandConvert.class); + + ProductBrandDO convert(ProductBrandCreateReqVO bean); + + ProductBrandDO convert(ProductBrandUpdateReqVO bean); + + ProductBrandRespVO convert(ProductBrandDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java deleted file mode 100644 index 2b2cfe99c..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.product.convert.category; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; - -/** - * 商品分类 Convert - * - * @author 芋道源码 - */ -@Mapper -public interface CategoryConvert { - - CategoryConvert INSTANCE = Mappers.getMapper(CategoryConvert.class); - - CategoryDO convert(CategoryCreateReqVO bean); - - CategoryDO convert(CategoryUpdateReqVO bean); - - CategoryRespVO convert(CategoryDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - List convertList02(List list); - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/ProductCategoryConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/ProductCategoryConvert.java new file mode 100644 index 000000000..ae01ca9d5 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/ProductCategoryConvert.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.product.convert.category; + +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryRespVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.app.category.vo.AppCategoryRespVO; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 商品分类 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface ProductCategoryConvert { + + ProductCategoryConvert INSTANCE = Mappers.getMapper(ProductCategoryConvert.class); + + ProductCategoryDO convert(ProductCategoryCreateReqVO bean); + + ProductCategoryDO convert(ProductCategoryUpdateReqVO bean); + + ProductCategoryRespVO convert(ProductCategoryDO bean); + + List convertList(List list); + + List convertList03(List list); +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java index c44cdc841..52b82a2ea 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java @@ -1,13 +1,14 @@ package cn.iocoder.yudao.module.product.convert.property; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; - +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyUpdateReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; + +import java.util.List; /** * 规格名称 Convert @@ -23,12 +24,10 @@ public interface ProductPropertyConvert { ProductPropertyDO convert(ProductPropertyUpdateReqVO bean); - ProductPropertyRespVO convert(ProductPropertyDO bean); + ProductPropertyAndValueRespVO convert(ProductPropertyDO bean); - List convertList(List list); + List convertList(List list); - PageResult convertPage(PageResult page); - - List convertList02(List list); + PageResult convertPage(PageResult page); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java index 447b9a5ea..331ad623f 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java @@ -4,10 +4,12 @@ import java.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.*; /** * 规格值 Convert diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java index 232ce3178..0ee468d36 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java @@ -1,17 +1,13 @@ package cn.iocoder.yudao.module.product.convert.sku; -import java.util.*; - -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Named; import org.mapstruct.factory.Mappers; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; -import org.springframework.util.StringUtils; + +import java.util.List; /** * 商品sku Convert @@ -23,38 +19,13 @@ public interface ProductSkuConvert { ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class); - @Mapping(source = "properties", target = "properties", qualifiedByName = "translateStringFromList") - ProductSkuDO convert(ProductSkuCreateReqVO bean); + ProductSkuDO convert(ProductSkuCreateOrUpdateReqVO bean); - @Mapping(source = "properties", target = "properties", qualifiedByName = "translateStringFromList") - ProductSkuDO convert(ProductSkuUpdateReqVO bean); - - @Mapping(source = "properties", target = "properties", qualifiedByName = "tokenizeToBeanArray") + @Mapping(source = "properties", target = "properties") ProductSkuRespVO convert(ProductSkuDO bean); - @Mapping(source = "properties", target = "properties", qualifiedByName = "tokenizeToExcelBeanArray") - ProductSkuExcelVO convertToExcelVO(ProductSkuDO bean); - List convertList(List list); - List convertSkuDOList(List list); + List convertSkuDOList(List list); - PageResult convertPage(PageResult page); - - List convertList02(List list); - - @Named("tokenizeToBeanArray") - default List translatePropertyArrayFromString(String properties) { - return JSONUtil.toList(properties, ProductSkuBaseVO.Property.class); - } - - @Named("tokenizeToExcelBeanArray") - default List translateExcelPropertyArrayFromString(String properties) { - return JSONUtil.toList(properties, ProductSkuExcelVO.Property.class); - } - - @Named("translateStringFromList") - default String translatePropertyStringFromList(List properties) { - return JSONUtil.toJsonStr(properties); - } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java index 92efaf0e8..2fb6639ab 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java @@ -1,16 +1,14 @@ package cn.iocoder.yudao.module.product.convert.spu; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; -import org.springframework.util.StringUtils; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; /** * 商品spu Convert @@ -22,31 +20,18 @@ public interface ProductSpuConvert { ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class); - @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "translatePicUrlsFromStringList") ProductSpuDO convert(ProductSpuCreateReqVO bean); - @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "translatePicUrlsFromStringList") - ProductSpuDO convert(SpuUpdateReqVO bean); + ProductSpuDO convert(ProductSpuUpdateReqVO bean); - @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray") - SpuRespVO convert(ProductSpuDO bean); + ProductSpuRespVO convert(ProductSpuDO bean); - @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray") - SpuExcelVO convertToExcelVO(ProductSpuDO bean); + List convertList(List list); - List convertList(List list); + PageResult convertPage(PageResult page); - PageResult convertPage(PageResult page); + ProductSpuPageReqVO convert(AppSpuPageReqVO bean); - List convertList02(List list); + AppSpuPageRespVO convertAppResp(ProductSpuDO list); - @Named("tokenizeToStringArray") - default List translatePicUrlsArrayFromString(String picUrls) { - return Arrays.asList(StringUtils.tokenizeToStringArray(picUrls, ",")); - } - - @Named("translatePicUrlsFromStringList") - default String translatePicUrlsFromList(List picUrls) { - return StringUtils.collectionToCommaDelimitedString(picUrls); - } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/BrandDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/ProductBrandDO.java similarity index 83% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/BrandDO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/ProductBrandDO.java index 2cbcee877..9775f36a5 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/BrandDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/ProductBrandDO.java @@ -6,9 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -// TODO @JeromeSoar:Product 前缀 /** - * 品牌 DO + * 商品品牌 DO * * @author 芋道源码 */ @@ -19,17 +18,13 @@ import lombok.*; @Builder @NoArgsConstructor @AllArgsConstructor -public class BrandDO extends BaseDO { +public class ProductBrandDO extends BaseDO { /** * 品牌编号 */ @TableId private Long id; - /** - * 分类编号 - */ - private Long categoryId; /** * 品牌名称 */ @@ -37,7 +32,7 @@ public class BrandDO extends BaseDO { /** * 品牌图片 */ - private String bannerUrl; + private String picUrl; /** * 品牌排序 */ @@ -53,4 +48,6 @@ public class BrandDO extends BaseDO { */ private Integer status; + // TODO 芋艿:firstLetter 首字母 + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/CategoryDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java similarity index 66% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/CategoryDO.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java index c6eb655ab..6671e9b44 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/CategoryDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/ProductCategoryDO.java @@ -6,10 +6,13 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -// TODO @JeromeSoar:Product 前缀 /** * 商品分类 DO * + * 商品分类一共两类: + * 1)一级分类:{@link #parentId} 等于 0 + * 2)二级 + 三级分类:{@link #parentId} 不等于 0 + * * @author 芋道源码 */ @TableName("product_category") @@ -19,7 +22,12 @@ import lombok.*; @Builder @NoArgsConstructor @AllArgsConstructor -public class CategoryDO extends BaseDO { +public class ProductCategoryDO extends BaseDO { + + /** + * 父分类编号 - 根分类 + */ + public static final Long PARENT_ID_NULL = 0L; /** * 分类编号 @@ -35,15 +43,12 @@ public class CategoryDO extends BaseDO { */ private String name; /** - * 分类图标 - */ - private String icon; - /** - * 分类 Banner 图片 + * 分类图片 * - * 第一层的商品分类,会有该字段,用于用户 App 展示 + * 一级分类:推荐 200 x 100 分辨率 + * 二级 + 三级分类:推荐 100 x 100 分辨率 */ - private String bannerUrl; + private String picUrl; /** * 分类排序 */ @@ -59,4 +64,6 @@ public class CategoryDO extends BaseDO { */ private Integer status; + // TODO 芋艿:is_recommend 是否首页推荐:1-是;0-否 + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java new file mode 100644 index 000000000..30db12819 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/comment/ProductCommentDO.java @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.comment; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.enums.comment.ProductCommentAuditStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.Date; +import java.util.List; + +/** + * 商品评论 DO + * + * @author 芋道源码 + */ +@TableName("product_comment") +@KeySequence("product_comment_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductCommentDO extends BaseDO { + + /** + * 评论编号,主键自增 + */ + @TableId + private Long id; + /** + * 商品 SPU 编号 + * + * 关联 {@link ProductSpuDO#getId()} + */ + private Long spuId; + /** + * 交易订单编号 + * + * 关联 TradeOrderDO 的 id 编号 + */ + private Long orderId; + /** + * 交易订单项编号 + * + * 关联 TradeOrderItemDO 的 id 编号 + */ + private Long orderItemId; + /** + * 审核状态 + * + * 枚举 {@link ProductCommentAuditStatusEnum} + */ + private Integer auditStatus; + + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 用户 IP + */ + private String userIp; + /** + * 是否匿名 + */ + private Boolean anonymous; + /** + * 评论内容 + */ + private String content; + /** + * 评论图片地址数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List picUrls; + /** + * 描述相符星级 + * + * 1-5 星 + */ + private Integer descriptionScore; + /** + * 商品评论星级 + * + * 1-5 星 + */ + private Integer productScore; + /** + * 服务评论星级 + * + * 1-5 星 + */ + private Integer serviceScore; + /** + * 物流评论星级 + * + * 1-5 星 + */ + private Integer expressComment; + + /** + * 商家是否回复 + */ + private Boolean replied; + /** + * 商家回复内容 + */ + private String replyContent; + /** + * 商家回复时间 + */ + private Date replyTime; + + /** + * 有用的计数 + * + * 其他用户看到评论时,可点击「有用」按钮 + */ + private Integer usefulCount; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/delivery/DeliveryTemplateDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/delivery/DeliveryTemplateDO.java new file mode 100644 index 000000000..70445dc01 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/delivery/DeliveryTemplateDO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 配送模板 SPU DO + * + * @author 芋道源码 + */ +@TableName("delivery_template") +@KeySequence("delivery_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeliveryTemplateDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/favorite/ProductFavoriteDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/favorite/ProductFavoriteDO.java new file mode 100644 index 000000000..54d4b31ac --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/favorite/ProductFavoriteDO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.favorite; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商品收藏 DO + * + * @author 芋道源码 + */ +@TableName("product_favorite") +@KeySequence("product_favorite_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductFavoriteDO extends BaseDO { + + /** + * 编号,主键自增 + */ + @TableId + private Long id; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 商品 SPU 编号 + * + * 关联 {@link ProductSpuDO#getId()} + */ + private Long spuId; + + // TODO 芋艿:type 1 收藏;2 点赞 + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupBindDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupBindDO.java new file mode 100644 index 000000000..2e3b63e59 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupBindDO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.group; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商品分组的绑定 DO + * + * @author 芋道源码 + */ +@TableName("product_group_bind") +@KeySequence("product_group_bind_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductGroupBindDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId + private Long id; + /** + * 商品分组编号 + * + * 关联 {@link ProductGroupDO#getId()} + */ + private Long groupId; + /** + * 商品 SPU 编号 + * + * 关联 {@link ProductSpuDO#getId()} + */ + private Long spuId; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupDO.java new file mode 100644 index 000000000..605e8c38a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/group/ProductGroupDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.group; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.enums.group.ProductGroupStyleEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商品分组 DO + * + * @author 芋道源码 + */ +@TableName("product_group") +@KeySequence("product_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductGroupDO extends BaseDO { + + /** + * 商品分组编号,自增 + */ + @TableId + private Long id; + /** + * 分组名称 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 商品数量 + */ + private Integer count; + /** + * 排序 + */ + private Integer sort; + /** + * 风格,用于 APP 首页展示商品的样式 + * + * 枚举 {@link ProductGroupStyleEnum} + */ + private Integer style; + /** + * 是否默认 + * + * true - 系统默认,不允许删除 + * false - 自定义,允许删除 + */ + private Boolean defaulted; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java index 5255c36ed..608b248fe 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java @@ -32,10 +32,12 @@ public class ProductPropertyDO extends BaseDO { */ private String name; /** - * 状态: 0 开启 ,1 禁用 + * 状态 * - * {@link CommonStatusEnum} + * 枚举 {@link CommonStatusEnum} */ private Integer status; + // TODO 芋艿:rule;规格属性 (发布商品时,和 SKU 关联);规格参数(搜索商品时,与 Category 关联搜索) + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java index 0203939cc..007b95a4f 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.product.dal.dataobject.property; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -28,9 +29,9 @@ public class ProductPropertyValueDO extends BaseDO { @TableId private Long id; /** - * 规格键 id + * 规格键编号 * - * TODO @franky:加个 关联 {@link ProductPropertyDO#getId()} ,这样就能更好的知道 + * 关联 {@link ProductPropertyDO#getId()} */ private Long propertyId; /** @@ -38,7 +39,9 @@ public class ProductPropertyValueDO extends BaseDO { */ private String name; /** - * 状态: 1 开启 ,2 禁用 + * 状态 + * + * 枚举 {@link CommonStatusEnum} */ private Integer status; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/search/ProductHotSearchDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/search/ProductHotSearchDO.java new file mode 100644 index 000000000..3d5cf9101 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/search/ProductHotSearchDO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.search; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商品热搜关键字 DO + * + * @author 芋道源码 + */ +@TableName("product_hot_search") +@KeySequence("product_hot_search_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductHotSearchDO extends BaseDO { + + /** + * 编号,主键自增 + */ + @TableId + private Long id; + /** + * 关键字 + */ + private String name; + /** + * 内容 + */ + private String content; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/shop/ShopDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/shop/ShopDO.java new file mode 100644 index 000000000..1c702da91 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/shop/ShopDO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.shop; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +// TODO 芋艿:待设计 +/** + * 店铺 DO + * + * @author 芋道源码 + */ +@TableName("shop") +@KeySequence("shop_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ShopDO extends BaseDO { + + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java index 1ffaaeef2..fc7bbe9ec 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java @@ -1,17 +1,26 @@ package cn.iocoder.yudao.module.product.dal.dataobject.sku; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import lombok.*; +import java.util.List; + /** - * 商品sku DO + * 商品 SKU DO * * @author 芋道源码 */ -@TableName("product_sku") +@TableName(value = "product_sku",autoResultMap = true) @KeySequence("product_sku_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @@ -22,33 +31,39 @@ import lombok.*; public class ProductSkuDO extends BaseDO { /** - * 主键 + * 商品 SKU 编号,自增 */ @TableId private Long id; /** - * spu编号 + * 商品 SKU 名字 + */ + private String name; + /** + * SPU 编号 + *

+ * 关联 {@link ProductSpuDO#getId()} */ private Long spuId; /** - * 规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }] + * 规格值数组,JSON 格式 */ - // TODO franky:可以定义一个内部的 Property 类,然后 List - private String properties; + @TableField(typeHandler = PropertyTypeHandler.class) + private List properties; /** * 销售价格,单位:分 */ private Integer price; /** - * 原价,单位:分 + * 市场价,单位:分 */ - private Integer originalPrice; + private Integer marketPrice; /** - * 成本价,单位: 分 + * 成本价,单位:分 */ private Integer costPrice; /** - * 条形码 + * SKU 的条形码 */ private String barCode; /** @@ -56,9 +71,77 @@ public class ProductSkuDO extends BaseDO { */ private String picUrl; /** - * 状态: 0-正常 1-禁用 + * SKU 状态 + *

+ * 枚举 {@link CommonStatusEnum} */ private Integer status; + /** + * 库存 + */ + private Integer stock; + /** + * 预警预存 + */ + private Integer warnStock; + /** + * 商品重量,单位:kg 千克 + */ + private Double weight; + /** + * 商品体积,单位:m^3 平米 + */ + private Double volume; + + /** + * 商品属性 + */ + @Data + public static class Property { + + /** + * 属性编号 + *

+ * 关联 {@link ProductPropertyDO#getId()} + */ + private Long propertyId; + /** + * 属性值编号 + *

+ * 关联 {@link ProductPropertyValueDO#getId()} + */ + private Long valueId; + + } + + // TODO @芋艿:可以找一些新的思路 + public static class PropertyTypeHandler extends AbstractJsonTypeHandler { + + @Override + protected Object parse(String json) { + return JsonUtils.parseArray(json, Property.class); + } + + @Override + protected String toJson(Object obj) { + return JsonUtils.toJsonString(obj); + } + + } + + // TODO ========== 待定字段:yv ========= + // TODO brokerage:一级返佣 + // TODO brokerage_two:二级返佣 + // TODO pink_price:拼团价 + // TODO pink_stock:拼团库存 + // TODO seckill_price:秒杀价 + // TODO seckill_stock:秒杀库存 + // TODO integral:需要积分 + + // TODO ========== 待定字段:cf ========= + // TODO type 活动显示排序 0=默认 1=秒 2=砍价 3=拼团 + // TODO quota 活动限购数量 + // TODO quota_show 活动限购数量显示 } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java index 2da638077..a22b68a2c 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java @@ -1,17 +1,26 @@ package cn.iocoder.yudao.module.product.dal.dataobject.spu; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.*; +import java.util.List; + /** - * 商品spu DO + * 商品 SPU DO * * @author 芋道源码 */ -@TableName("product_spu") +@TableName(value = "product_spu", autoResultMap = true) @KeySequence("product_spu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @@ -22,50 +31,182 @@ import lombok.*; public class ProductSpuDO extends BaseDO { /** - * 主键 + * 商品 SPU 编号,自增 */ @TableId private Long id; + + // ========== 基本信息 ========= + /** * 商品名称 */ private String name; /** - * 卖点 + * 商品编码 + */ + private String code; + /** + * 商品卖点 */ private String sellPoint; /** - * 描述 + * 商品详情 */ private String description; /** - * 分类id + * 商品分类编号 + * + * 关联 {@link ProductCategoryDO#getId()} */ private Long categoryId; /** - * 商品主图地址,* 数组,以逗号分隔,最多上传15张 + * 商品品牌编号 + * + * 关联 {@link ProductBrandDO#getId()} */ - // TODO franky:List。可以参考别的模块,怎么处理这种类型的哈 - private String picUrls; + private Long brandId; + /** + * 商品图片的数组 + * + * 1. 第一张图片将作为商品主图,支持同时上传多张图; + * 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片; + * 3. 至少 1 张,最多上传 10 张 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List picUrls; + /** + * 商品视频 + */ + private String videoUrl; + /** * 排序字段 */ private Integer sort; /** - * 点赞初始人数 + * 商品状态 + * + * 枚举 {@link ProductSpuStatusEnum} */ - private Integer likeCount; + private Integer status; + + // ========== SKU 相关字段 ========= + /** - * 价格 单位使用:分 + * 规格类型 + * + * 枚举 {@link ProductSpuSpecTypeEnum} */ - private Integer price; + private Integer specType; /** - * 库存数量 + * 最小价格,单位使用:分 + * + * 基于其对应的 {@link ProductSkuDO#getPrice()} 最小值 */ - private Integer quantity; + private Integer minPrice; /** - * 上下架状态: 0 上架(开启) 1 下架(禁用) + * 最大价格,单位使用:分 + * + * 基于其对应的 {@link ProductSkuDO#getPrice()} 最大值 */ - private Boolean status; + private Integer maxPrice; + /** + * 市场价,单位使用:分 + * + * 基于其对应的 {@link ProductSkuDO#getMarketPrice()} 最大值 + */ + private Integer marketPrice; + /** + * 总库存 + * + * 基于其对应的 {@link ProductSkuDO#getStock()} 求和 + */ + private Integer totalStock; + /** + * 是否展示库存 + */ + private Boolean showStock; + + // ========== 统计相关字段 ========= + + /** + * 商品销量 + */ + private Integer salesCount; + /** + * 虚拟销量 + */ + private Integer virtualSalesCount; + /** + * 商品点击量 + */ + private Integer clickCount; + + // ========== 物流相关字段 ========= + + // TODO 芋艿:稍后完善物流的字段 +// /** +// * 配送方式 +// * +// * 枚举 {@link DeliveryModeEnum} +// */ +// private Integer deliveryMode; +// /** +// * 配置模板编号 +// * +// * 关联 {@link DeliveryTemplateDO#getId()} +// */ +// private Long deliveryTemplateId; + + // TODO ========== 待定字段:yv ========= + // TODO vip_price 会员价格 + // TODO postage 邮费 + // TODO is_postage 是否包邮 + // TODO unit_name 单位 + // TODO is_new 商户是否代理 + // TODO give_integral 获得积分 + // TODO is_integral 是开启积分兑换 + // TODO integral 所需积分 + // TODO is_seckill 秒杀状态 + // TODO is_bargain 砍价状态 + // TODO code_path 产品二维码地址 + // TODO is_sub 是否分佣 + + // TODO ↓↓ 芋艿 ↓↓ 看起来走分组更合适? + // TODO is_hot 是否热卖 + // TODO is_benefit 是否优惠 + // TODO is_best 是否精品 + // TODO is_new 是否新品 + // TODO is_good 是否优品推荐 + + // TODO ========== 待定字段:cf ========= + // TODO source_link 淘宝京东1688类型 + // TODO activity 活动显示排序 0=默认 1=秒 2=砍价 3=拼团 + + // TODO ========== 待定字段:lf ========= + + // TODO free_shipping_type:运费类型:1-包邮;2-统一运费;3-运费模板 + // TODO free_shipping:统一运费金额 + // TODO free_shipping_template_id:运费模板 + // TODO is_commission:分销佣金:1-开启;0-不开启;first_ratio second_ratio three_ratio + // TODO is_share_bouns:区域股东分红:1-开启;0-不开启;region_ratio;shareholder_ratio + + // TODO is_new:新品推荐:1-是;0-否 + // TODO is_best:好物优选:1-是;0-否 + // TODO is_like:猜你喜欢:1-是;0-否 + + // TODO is_team:是否开启拼团[0=否, 1=是] + // TODO is_integral:积分抵扣:1-开启;0-不开启 + // TODO is_member:会员价:1-开启;0-不开启 + // TODO give_integral_type:赠送积分类型:0-不赠送;1-赠送固定积分;2-按比例赠送积分 + // TODO give_integral:赠送积分; + + // TODO poster:商品自定义海报 + + // TODO ========== 待定字段:laoji ========= + // TODO productType 1 - 普通商品 2 - 预售商品;可能和 type 合并不错 + // TODO productUnit 商品单位 + // TODO extJson 扩展信息;例如说,预售商品的信息 } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/BrandMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/BrandMapper.java deleted file mode 100644 index cf7c5f69a..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/BrandMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.product.dal.mysql.brand; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; - -/** - * 品牌 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface BrandMapper extends BaseMapperX { - - default PageResult selectPage(BrandPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(BrandDO::getCategoryId, reqVO.getCategoryId()) - .likeIfPresent(BrandDO::getName, reqVO.getName()) - .eqIfPresent(BrandDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BrandDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BrandDO::getId)); - } - - default List selectList(BrandExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(BrandDO::getCategoryId, reqVO.getCategoryId()) - .likeIfPresent(BrandDO::getName, reqVO.getName()) - .eqIfPresent(BrandDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BrandDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BrandDO::getId)); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java new file mode 100644 index 000000000..a62df7dc8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/ProductBrandMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.product.dal.mysql.brand; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ProductBrandMapper extends BaseMapperX { + + default PageResult selectPage(ProductBrandPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ProductBrandDO::getName, reqVO.getName()) + .eqIfPresent(ProductBrandDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(ProductBrandDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(ProductBrandDO::getId)); + } + + + default List selectList(ProductBrandListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(ProductBrandDO::getName, reqVO.getName())); + } + + default ProductBrandDO selectByName(String name) { + return selectOne(ProductBrandDO::getName, name); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/CategoryMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/CategoryMapper.java deleted file mode 100644 index e0f798e18..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/CategoryMapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.product.dal.mysql.category; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * 商品分类 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface CategoryMapper extends BaseMapperX { - - default PageResult selectPage(CategoryPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(CategoryDO::getName, reqVO.getName()) - .eqIfPresent(CategoryDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(CategoryDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CategoryDO::getId)); - } - - default List selectList(CategoryExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(CategoryDO::getName, reqVO.getName()) - .eqIfPresent(CategoryDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(CategoryDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(CategoryDO::getId)); - } - - default Long selectCountByParentId(Long parentId) { - return selectCount(CategoryDO::getParentId, parentId); - } -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/ProductCategoryMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/ProductCategoryMapper.java new file mode 100644 index 000000000..8130d5a46 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/ProductCategoryMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.product.dal.mysql.category; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 商品分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ProductCategoryMapper extends BaseMapperX { + + default List selectList(ProductCategoryListReqVO listReqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(ProductCategoryDO::getName, listReqVO.getName()) + .orderByDesc(ProductCategoryDO::getId)); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(ProductCategoryDO::getParentId, parentId); + } + + default List selectListByStatus(Integer status) { + return selectList(ProductCategoryDO::getStatus, status); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java index 28f4ea51c..bd088466f 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java @@ -1,13 +1,11 @@ package cn.iocoder.yudao.module.product.dal.mysql.property; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; /** * 规格名称 Mapper @@ -25,12 +23,4 @@ public interface ProductPropertyMapper extends BaseMapperX { .orderByDesc(ProductPropertyDO::getId)); } - default List selectList(ProductPropertyExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(ProductPropertyDO::getName, reqVO.getName()) - .eqIfPresent(ProductPropertyDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ProductPropertyDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ProductPropertyDO::getId)); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/propertyvalue/ProductPropertyValueMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java similarity index 95% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/propertyvalue/ProductPropertyValueMapper.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java index 5c11a9f42..6c5fc7570 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/propertyvalue/ProductPropertyValueMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyValueMapper.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.product.dal.mysql.propertyvalue; +package cn.iocoder.yudao.module.product.dal.mysql.property; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java index 2f2ced33c..cc45e5764 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java @@ -1,55 +1,27 @@ package cn.iocoder.yudao.module.product.dal.mysql.sku; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuExportReqVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** - * 商品sku Mapper + * 商品 SKU Mapper * * @author 芋道源码 */ @Mapper public interface ProductSkuMapper extends BaseMapperX { - default PageResult selectPage(ProductSkuPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(ProductSkuDO::getSpuId, reqVO.getSpuId()) - .eqIfPresent(ProductSkuDO::getProperties, reqVO.getProperties()) - .eqIfPresent(ProductSkuDO::getPrice, reqVO.getPrice()) - .eqIfPresent(ProductSkuDO::getOriginalPrice, reqVO.getOriginalPrice()) - .eqIfPresent(ProductSkuDO::getCostPrice, reqVO.getCostPrice()) - .eqIfPresent(ProductSkuDO::getBarCode, reqVO.getBarCode()) - .eqIfPresent(ProductSkuDO::getPicUrl, reqVO.getPicUrl()) - .eqIfPresent(ProductSkuDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ProductSkuDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ProductSkuDO::getId)); - } - - default List selectList(ProductSkuExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(ProductSkuDO::getSpuId, reqVO.getSpuId()) - .eqIfPresent(ProductSkuDO::getProperties, reqVO.getProperties()) - .eqIfPresent(ProductSkuDO::getPrice, reqVO.getPrice()) - .eqIfPresent(ProductSkuDO::getOriginalPrice, reqVO.getOriginalPrice()) - .eqIfPresent(ProductSkuDO::getCostPrice, reqVO.getCostPrice()) - .eqIfPresent(ProductSkuDO::getBarCode, reqVO.getBarCode()) - .eqIfPresent(ProductSkuDO::getPicUrl, reqVO.getPicUrl()) - .eqIfPresent(ProductSkuDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ProductSkuDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ProductSkuDO::getId)); - } - // TODO @franky:方法名 selectList; 可以直接调用 selectList - default List selectBySpuIds(List spuIds) { - return selectList(new LambdaQueryWrapperX() - .inIfPresent(ProductSkuDO::getSpuId, spuIds)); + default List selectListBySpuIds(List spuIds) { + return selectList(ProductSkuDO::getSpuId, spuIds); + } + + default List selectListBySpuId(Long spuId) { + return selectList(ProductSkuDO::getSpuId, spuId); } default void deleteBySpuId(Long spuId) { @@ -58,4 +30,5 @@ public interface ProductSkuMapper extends BaseMapperX { .eqIfPresent(ProductSkuDO::getSpuId, spuId); delete(lambdaQueryWrapperX); } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java index 72fb68b29..1755695cb 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java @@ -1,13 +1,11 @@ package cn.iocoder.yudao.module.product.dal.mysql.spu; -import java.util.*; - import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; /** * 商品spu Mapper @@ -17,36 +15,15 @@ import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; @Mapper public interface ProductSpuMapper extends BaseMapperX { - default PageResult selectPage(SpuPageReqVO reqVO) { + default PageResult selectPage(ProductSpuPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .likeIfPresent(ProductSpuDO::getName, reqVO.getName()) .eqIfPresent(ProductSpuDO::getSellPoint, reqVO.getSellPoint()) - .eqIfPresent(ProductSpuDO::getDescription, reqVO.getDescription()) .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) .eqIfPresent(ProductSpuDO::getPicUrls, reqVO.getPicUrls()) - .eqIfPresent(ProductSpuDO::getSort, reqVO.getSort()) - .eqIfPresent(ProductSpuDO::getLikeCount, reqVO.getLikeCount()) - .eqIfPresent(ProductSpuDO::getPrice, reqVO.getPrice()) - .eqIfPresent(ProductSpuDO::getQuantity, reqVO.getQuantity()) .eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus()) .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ProductSpuDO::getId)); - } - - default List selectList(SpuExportReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(ProductSpuDO::getName, reqVO.getName()) - .eqIfPresent(ProductSpuDO::getSellPoint, reqVO.getSellPoint()) - .eqIfPresent(ProductSpuDO::getDescription, reqVO.getDescription()) - .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) - .eqIfPresent(ProductSpuDO::getPicUrls, reqVO.getPicUrls()) - .eqIfPresent(ProductSpuDO::getSort, reqVO.getSort()) - .eqIfPresent(ProductSpuDO::getLikeCount, reqVO.getLikeCount()) - .eqIfPresent(ProductSpuDO::getPrice, reqVO.getPrice()) - .eqIfPresent(ProductSpuDO::getQuantity, reqVO.getQuantity()) - .eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(ProductSpuDO::getId)); + .orderByDesc(ProductSpuDO::getSort)); } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java index 4e80cc2bc..117f35931 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java @@ -1,7 +1,8 @@ /** - * TODO + * trade 模块,主要实现交易相关功能 + * 例如:订单、退款、购物车等功能。 * - * @author JeromeSoar - * @since 2022-04-24 + * 1. Controller URL:以 /trade/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 trade_ 开头,方便在数据库中区分 */ -package cn.iocoder.yudao.module.product; \ No newline at end of file +package cn.iocoder.yudao.module.product; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImpl.java deleted file mode 100644 index dedda3ae2..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.product.service.brand; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; - -import java.util.*; -import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import cn.iocoder.yudao.module.product.convert.brand.BrandConvert; -import cn.iocoder.yudao.module.product.dal.mysql.brand.BrandMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; - -/** - * 品牌 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class BrandServiceImpl implements BrandService { - - @Resource - private BrandMapper brandMapper; - - @Override - public Long createBrand(BrandCreateReqVO createReqVO) { - // 插入 - BrandDO brand = BrandConvert.INSTANCE.convert(createReqVO); - brandMapper.insert(brand); - // 返回 - return brand.getId(); - } - - @Override - public void updateBrand(BrandUpdateReqVO updateReqVO) { - // 校验存在 - this.validateBrandExists(updateReqVO.getId()); - // 更新 - BrandDO updateObj = BrandConvert.INSTANCE.convert(updateReqVO); - brandMapper.updateById(updateObj); - } - - @Override - public void deleteBrand(Long id) { - // 校验存在 - this.validateBrandExists(id); - // 删除 - brandMapper.deleteById(id); - } - - private void validateBrandExists(Long id) { - if (brandMapper.selectById(id) == null) { - throw exception(BRAND_NOT_EXISTS); - } - } - - @Override - public BrandDO getBrand(Long id) { - return brandMapper.selectById(id); - } - - @Override - public List getBrandList(Collection ids) { - return brandMapper.selectBatchIds(ids); - } - - @Override - public PageResult getBrandPage(BrandPageReqVO pageReqVO) { - return brandMapper.selectPage(pageReqVO); - } - - @Override - public List getBrandList(BrandExportReqVO exportReqVO) { - return brandMapper.selectList(exportReqVO); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java similarity index 51% rename from yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandService.java rename to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java index 2cb356fed..a90ec8a87 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandService.java @@ -1,17 +1,19 @@ package cn.iocoder.yudao.module.product.service.brand; -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; /** - * 品牌 Service 接口 + * 商品品牌 Service 接口 * * @author 芋道源码 */ -public interface BrandService { +public interface ProductBrandService { /** * 创建品牌 @@ -19,14 +21,14 @@ public interface BrandService { * @param createReqVO 创建信息 * @return 编号 */ - Long createBrand(@Valid BrandCreateReqVO createReqVO); + Long createBrand(@Valid ProductBrandCreateReqVO createReqVO); /** * 更新品牌 * * @param updateReqVO 更新信息 */ - void updateBrand(@Valid BrandUpdateReqVO updateReqVO); + void updateBrand(@Valid ProductBrandUpdateReqVO updateReqVO); /** * 删除品牌 @@ -41,7 +43,7 @@ public interface BrandService { * @param id 编号 * @return 品牌 */ - BrandDO getBrand(Long id); + ProductBrandDO getBrand(Long id); /** * 获得品牌列表 @@ -49,7 +51,22 @@ public interface BrandService { * @param ids 编号 * @return 品牌列表 */ - List getBrandList(Collection ids); + List getBrandList(Collection ids); + + /** + * 获得品牌列表 + * + * @param listReqVO 请求参数 + * @return 品牌列表 + */ + List getBrandList(ProductBrandListReqVO listReqVO); + + /** + * 验证选择的商品分类是否合法 + * + * @param id 分类编号 + */ + void validateProductBrand(Long id); /** * 获得品牌分页 @@ -57,14 +74,6 @@ public interface BrandService { * @param pageReqVO 分页查询 * @return 品牌分页 */ - PageResult getBrandPage(BrandPageReqVO pageReqVO); - - /** - * 获得品牌列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 品牌列表 - */ - List getBrandList(BrandExportReqVO exportReqVO); + PageResult getBrandPage(ProductBrandPageReqVO pageReqVO); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java new file mode 100644 index 000000000..c7a3e5198 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImpl.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.product.service.brand; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO; +import cn.iocoder.yudao.module.product.convert.brand.ProductBrandConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import cn.iocoder.yudao.module.product.dal.mysql.brand.ProductBrandMapper; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; + +/** + * 品牌 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ProductBrandServiceImpl implements ProductBrandService { + + @Resource + private ProductBrandMapper brandMapper; + + @Override + public Long createBrand(ProductBrandCreateReqVO createReqVO) { + // 校验 + validateBrandNameUnique(null, createReqVO.getName()); + + // 插入 + ProductBrandDO brand = ProductBrandConvert.INSTANCE.convert(createReqVO); + brandMapper.insert(brand); + // 返回 + return brand.getId(); + } + + @Override + public void updateBrand(ProductBrandUpdateReqVO updateReqVO) { + // 校验存在 + validateBrandExists(updateReqVO.getId()); + validateBrandNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 更新 + ProductBrandDO updateObj = ProductBrandConvert.INSTANCE.convert(updateReqVO); + brandMapper.updateById(updateObj); + } + + @Override + public void deleteBrand(Long id) { + // 校验存在 + validateBrandExists(id); + // 删除 + brandMapper.deleteById(id); + } + + private void validateBrandExists(Long id) { + if (brandMapper.selectById(id) == null) { + throw exception(BRAND_NOT_EXISTS); + } + } + + @VisibleForTesting + public void validateBrandNameUnique(Long id, String name) { + ProductBrandDO brand = brandMapper.selectByName(name); + if (brand == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(BRAND_NAME_EXISTS); + } + if (!brand.getId().equals(id)) { + throw exception(BRAND_NAME_EXISTS); + } + } + + @Override + public ProductBrandDO getBrand(Long id) { + return brandMapper.selectById(id); + } + + @Override + public List getBrandList(Collection ids) { + return brandMapper.selectBatchIds(ids); + } + + @Override + public List getBrandList(ProductBrandListReqVO listReqVO) { + return brandMapper.selectList(listReqVO); + } + + @Override + public void validateProductBrand(Long id) { + ProductBrandDO brand = brandMapper.selectById(id); + if (brand == null) { + throw exception(BRAND_NOT_EXISTS); + } + if (brand.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw exception(BRAND_DISABLED); + } + } + + @Override + public PageResult getBrandPage(ProductBrandPageReqVO pageReqVO) { + return brandMapper.selectPage(pageReqVO); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java deleted file mode 100644 index 4df2f19f9..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.product.service.category; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; - -// TODO @JeromeSoar:需要 Product 前缀 -/** - * 商品分类 Service 接口 - * - * @author 芋道源码 - */ -public interface CategoryService { - - /** - * 创建商品分类 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createCategory(@Valid CategoryCreateReqVO createReqVO); - - /** - * 更新商品分类 - * - * @param updateReqVO 更新信息 - */ - void updateCategory(@Valid CategoryUpdateReqVO updateReqVO); - - /** - * 删除商品分类 - * - * @param id 编号 - */ - void deleteCategory(Long id); - - /** - * 获得商品分类 - * - * @param id 编号 - * @return 商品分类 - */ - CategoryDO getCategory(Long id); - - /** - * 获得商品分类列表 - * - * @param ids 编号 - * @return 商品分类列表 - */ - List getCategoryList(Collection ids); - - /** - * 获得商品分类分页 - * - * @param pageReqVO 分页查询 - * @return 商品分类分页 - */ - PageResult getCategoryPage(CategoryPageReqVO pageReqVO); - - /** - * 获得商品分类列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 商品分类列表 - */ - List getCategoryList(CategoryExportReqVO exportReqVO); - - /** - * 获得商品分类列表 - * - * @param treeListReqVO 查询条件 - * @return 商品分类列表 - */ - List getCategoryTreeList(CategoryTreeListReqVO treeListReqVO); - - /** - * 验证选择的分类的合法性 - * @param categoryId 分类id - */ - void validatedCategoryById(Long categoryId); -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java deleted file mode 100644 index 3fa441b66..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java +++ /dev/null @@ -1,109 +0,0 @@ -package cn.iocoder.yudao.module.product.service.category; - -import cn.iocoder.yudao.framework.common.exception.ErrorCode; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.*; -import cn.iocoder.yudao.module.product.convert.category.CategoryConvert; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; -import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; - -/** - * 商品分类 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class CategoryServiceImpl implements CategoryService { - - @Resource - private CategoryMapper categoryMapper; - - @Override - public Long createCategory(CategoryCreateReqVO createReqVO) { - // 校验父分类存在 - this.validateCategoryExists(createReqVO.getParentId(), CATEGORY_PARENT_NOT_EXISTS); - // 插入 - CategoryDO category = CategoryConvert.INSTANCE.convert(createReqVO); - categoryMapper.insert(category); - // 返回 - return category.getId(); - } - - @Override - public void updateCategory(CategoryUpdateReqVO updateReqVO) { - // 校验父分类存在 - this.validateCategoryExists(updateReqVO.getParentId(), CATEGORY_PARENT_NOT_EXISTS); - // 校验分类是否存在 - this.validateCategoryExists(updateReqVO.getId(), CATEGORY_NOT_EXISTS); - // 更新 - CategoryDO updateObj = CategoryConvert.INSTANCE.convert(updateReqVO); - categoryMapper.updateById(updateObj); - } - - @Override - public void deleteCategory(Long id) { - // TODO 芋艿 补充只有不存在商品才可以删除 - // 校验分类是否存在 - CategoryDO categoryDO = this.validateCategoryExists(id, CATEGORY_NOT_EXISTS); - // 校验是否还有子分类 - if (categoryMapper.selectCountByParentId(categoryDO.getParentId()) > 0) { - throw ServiceExceptionUtil.exception(CATEGORY_EXISTS_CHILDREN); - } - // 删除 - categoryMapper.deleteById(id); - } - - private CategoryDO validateCategoryExists(Long id, ErrorCode errorCode) { - // TODO franky:0 要枚举哈 - if (id == 0) { - return new CategoryDO().setId(id); - } - CategoryDO categoryDO = categoryMapper.selectById(id); - if (categoryDO == null) { - throw exception(errorCode); - } - return categoryDO; - } - - @Override - public void validatedCategoryById(Long categoryId) { - this.validateCategoryExists(categoryId, CATEGORY_NOT_EXISTS); - } - - @Override - public CategoryDO getCategory(Long id) { - return categoryMapper.selectById(id); - } - - @Override - public List getCategoryList(Collection ids) { - return categoryMapper.selectBatchIds(ids); - } - - @Override - public PageResult getCategoryPage(CategoryPageReqVO pageReqVO) { - return categoryMapper.selectPage(pageReqVO); - } - - @Override - public List getCategoryList(CategoryExportReqVO exportReqVO) { - return categoryMapper.selectList(exportReqVO); - } - - @Override - public List getCategoryTreeList(CategoryTreeListReqVO treeListReqVO) { - return categoryMapper.selectList(treeListReqVO); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java new file mode 100644 index 000000000..118e8118b --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.product.service.category; + +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryUpdateReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 商品分类 Service 接口 + * + * @author 芋道源码 + */ +public interface ProductCategoryService { + + /** + * 创建商品分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCategory(@Valid ProductCategoryCreateReqVO createReqVO); + + /** + * 更新商品分类 + * + * @param updateReqVO 更新信息 + */ + void updateCategory(@Valid ProductCategoryUpdateReqVO updateReqVO); + + /** + * 删除商品分类 + * + * @param id 编号 + */ + void deleteCategory(Long id); + + /** + * 获得商品分类 + * + * @param id 编号 + * @return 商品分类 + */ + ProductCategoryDO getCategory(Long id); + + /** + * 获得商品分类列表 + * + * @param ids 编号 + * @return 商品分类列表 + */ + List getEnableCategoryList(Collection ids); + + /** + * 获得商品分类列表 + * + * @param listReqVO 查询条件 + * @return 商品分类列表 + */ + List getEnableCategoryList(ProductCategoryListReqVO listReqVO); + + /** + * 验证选择的商品分类的级别是否合法 + * 例如说,商品发布的时候,必须在第 3 级别 + * + * @param id 分类编号 + */ + void validateCategoryLevel(Long id); + + /** + * 获得开启状态的商品分类列表 + * + * @return 商品分类列表 + */ + List getEnableCategoryList(); + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java new file mode 100644 index 000000000..ebfe599d6 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.module.product.service.category; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryUpdateReqVO; +import cn.iocoder.yudao.module.product.convert.category.ProductCategoryConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import cn.iocoder.yudao.module.product.dal.mysql.category.ProductCategoryMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; + +/** + * 商品分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ProductCategoryServiceImpl implements ProductCategoryService { + + @Resource + private ProductCategoryMapper productCategoryMapper; + + @Override + public Long createCategory(ProductCategoryCreateReqVO createReqVO) { + // 校验父分类存在 + validateParentProductCategory(createReqVO.getParentId()); + + // 插入 + ProductCategoryDO category = ProductCategoryConvert.INSTANCE.convert(createReqVO); + productCategoryMapper.insert(category); + // 返回 + return category.getId(); + } + + @Override + public void updateCategory(ProductCategoryUpdateReqVO updateReqVO) { + // 校验分类是否存在 + validateProductCategoryExists(updateReqVO.getId()); + // 校验父分类存在 + validateParentProductCategory(updateReqVO.getParentId()); + + // 更新 + ProductCategoryDO updateObj = ProductCategoryConvert.INSTANCE.convert(updateReqVO); + productCategoryMapper.updateById(updateObj); + } + + @Override + public void deleteCategory(Long id) { + // 校验分类是否存在 + validateProductCategoryExists(id); + // 校验是否还有子分类 + if (productCategoryMapper.selectCountByParentId(id) > 0) { + throw exception(CATEGORY_EXISTS_CHILDREN); + } + // TODO 芋艿 补充只有不存在商品才可以删除 + // 删除 + productCategoryMapper.deleteById(id); + } + + private void validateParentProductCategory(Long id) { + // 如果是根分类,无需验证 + if (Objects.equals(id, ProductCategoryDO.PARENT_ID_NULL)) { + return; + } + // 父分类不存在 + ProductCategoryDO category = productCategoryMapper.selectById(id); + if (category == null) { + throw exception(CATEGORY_PARENT_NOT_EXISTS); + } + // 父分类不能是二级分类 + if (Objects.equals(id, ProductCategoryDO.PARENT_ID_NULL)) { + throw exception(CATEGORY_PARENT_NOT_FIRST_LEVEL); + } + } + + private void validateProductCategoryExists(Long id) { + ProductCategoryDO category = productCategoryMapper.selectById(id); + if (category == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + } + + @Override + public void validateCategoryLevel(Long id) { + Integer level = getProductCategoryLevel(id, 1); + if (level < 3){ + throw exception(CATEGORY_LEVEL_ERROR); + } + } + + // TODO @Luowenfeng:建议使用 for 循环,避免递归 + /** + * 获得商品分类的级别 + * + * @param id 商品分类的编号 + * @return 级别 + */ + private Integer getProductCategoryLevel(Long id, int level){ + ProductCategoryDO category = productCategoryMapper.selectById(id); + if (category == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + // TODO Luowenfeng:去掉是否开启,它不影响级别哈 + if (ObjectUtil.notEqual(category.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + throw exception(CATEGORY_DISABLED); + } + // TODO Luowenfeng:不使用 0 直接比较哈,使用枚举 + if (category.getParentId() == 0) { + return level; + } + return getProductCategoryLevel(category.getParentId(), ++level); + } + + @Override + public ProductCategoryDO getCategory(Long id) { + return productCategoryMapper.selectById(id); + } + + @Override + public List getEnableCategoryList(Collection ids) { + return productCategoryMapper.selectBatchIds(ids); + } + + @Override + public List getEnableCategoryList(ProductCategoryListReqVO listReqVO) { + return productCategoryMapper.selectList(listReqVO); + } + + @Override + public List getEnableCategoryList() { + return productCategoryMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java index a663a49ce..a87e15d70 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java @@ -1,10 +1,13 @@ package cn.iocoder.yudao.module.product.service.property; -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; /** * 规格名称 Service 接口 @@ -59,27 +62,21 @@ public interface ProductPropertyService { */ PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO); - /** - * 获得规格名称列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 规格名称列表 - */ - List getPropertyList(ProductPropertyExportReqVO exportReqVO); - /** * 获取属性及属性值列表 分页 * @param pageReqVO * @return */ - PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO); + PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO); - ProductPropertyRespVO getPropertyResp(Long id); + ProductPropertyAndValueRespVO getPropertyResp(Long id); /** - * 根据数据名id集合查询属性名以及属性值的集合 - * @param propertyIds 属性名id集合 - * @return + * 根据规格属性编号的集合,获得对应的规格 + 规格值的集合 + * + * @param ids 规格编号的集合 + * @return 对应的规格 + 规格值的集合 */ - List selectByIds(List propertyIds); + List getPropertyAndValueList(Collection ids); + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java index 9cb1a0da3..d48c8b1ff 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java @@ -1,15 +1,16 @@ package cn.iocoder.yudao.module.product.service.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.*; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; import cn.iocoder.yudao.module.product.convert.property.ProductPropertyConvert; import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyMapper; -import cn.iocoder.yudao.module.product.dal.mysql.propertyvalue.ProductPropertyValueMapper; +import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -49,7 +50,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { //插入属性值 List propertyValueList = createReqVO.getPropertyValueList(); List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); - productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(property.getId())); + productPropertyValueDOList.forEach(x-> x.setPropertyId(property.getId())); productPropertyValueMapper.insertBatch(productPropertyValueDOList); // 返回 return property.getId(); @@ -67,7 +68,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId()); List propertyValueList = updateReqVO.getPropertyValueList(); List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); - productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(updateReqVO.getId())); + productPropertyValueDOList.forEach(x-> x.setPropertyId(updateReqVO.getId())); productPropertyValueMapper.insertBatch(productPropertyValueDOList); } @@ -103,16 +104,11 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { } @Override - public List getPropertyList(ProductPropertyExportReqVO exportReqVO) { - return productPropertyMapper.selectList(exportReqVO); - } - - @Override - public PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO) { + public PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO) { //获取属性列表 PageResult pageResult = productPropertyMapper.selectPage(pageReqVO); - PageResult propertyRespVOPageResult = ProductPropertyConvert.INSTANCE.convertPage(pageResult); - List propertyIds = propertyRespVOPageResult.getList().stream().map(ProductPropertyRespVO::getId).collect(Collectors.toList()); + PageResult propertyRespVOPageResult = ProductPropertyConvert.INSTANCE.convertPage(pageResult); + List propertyIds = propertyRespVOPageResult.getList().stream().map(ProductPropertyAndValueRespVO::getId).collect(Collectors.toList()); //获取属性值列表 List productPropertyValueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); @@ -121,7 +117,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { propertyRespVOPageResult.getList().forEach(x->{ Long propertyId = x.getId(); List valueDOList = propertyValueRespVOList.stream().filter(v -> v.getPropertyId().equals(propertyId)).collect(Collectors.toList()); - x.setPropertyValueList(valueDOList); + x.setValues(valueDOList); }); return propertyRespVOPageResult; } @@ -131,25 +127,25 @@ public class ProductPropertyServiceImpl implements ProductPropertyService { } @Override - public ProductPropertyRespVO getPropertyResp(Long id) { + public ProductPropertyAndValueRespVO getPropertyResp(Long id) { //查询规格 ProductPropertyDO property = getProperty(id); - ProductPropertyRespVO propertyRespVO = ProductPropertyConvert.INSTANCE.convert(property); + ProductPropertyAndValueRespVO propertyRespVO = ProductPropertyConvert.INSTANCE.convert(property); //查询属性值 List valueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(Arrays.asList(id)); List propertyValueRespVOS = ProductPropertyValueConvert.INSTANCE.convertList(valueDOList); //组装 - propertyRespVO.setPropertyValueList(propertyValueRespVOS); + propertyRespVO.setValues(propertyValueRespVOS); return propertyRespVO; } @Override - public List selectByIds(List propertyIds) { - List productPropertyRespVO = ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(propertyIds)); + public List getPropertyAndValueList(Collection ids) { + List productPropertyRespVO = ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(ids)); //查询属性值 - List valueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); + List valueDOList = productPropertyValueMapper.selectBatchIds(ids); Map> propertyValuesMap = valueDOList.stream().collect(Collectors.groupingBy(ProductPropertyValueDO::getPropertyId)); - productPropertyRespVO.forEach(p -> p.setPropertyValueList(ProductPropertyValueConvert.INSTANCE.convertList(propertyValuesMap.get(p.getId())))); + productPropertyRespVO.forEach(p -> p.setValues(ProductPropertyValueConvert.INSTANCE.convertList(propertyValuesMap.get(p.getId())))); return productPropertyRespVO; } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java index 0396506ef..5316f4764 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java @@ -1,10 +1,6 @@ package cn.iocoder.yudao.module.product.service.sku; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuExportReqVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuPageReqVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import javax.validation.Valid; @@ -12,27 +8,12 @@ import java.util.Collection; import java.util.List; /** - * 商品sku Service 接口 + * 商品 SKU Service 接口 * * @author 芋道源码 */ public interface ProductSkuService { - /** - * 创建商品sku - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSku(@Valid ProductSkuCreateReqVO createReqVO); - - /** - * 更新商品sku - * - * @param updateReqVO 更新信息 - */ - void updateSku(@Valid ProductSkuUpdateReqVO updateReqVO); - /** * 删除商品sku * @@ -56,35 +37,28 @@ public interface ProductSkuService { */ List getSkuList(Collection ids); - /** - * 获得商品sku分页 - * - * @param pageReqVO 分页查询 - * @return 商品sku分页 - */ - PageResult getSkuPage(ProductSkuPageReqVO pageReqVO); - - /** - * 获得商品sku列表, 用于 Excel 导出 - * - * @param exportReqVO 查询条件 - * @return 商品sku列表 - */ - List getSkuList(ProductSkuExportReqVO exportReqVO); - /** * 对 sku 的组合的属性等进行合法性校验 * * @param list sku组合的集合 */ - void validateSkus(List list); + void validateSkus(List list, Integer specType); /** - * 批量保存 sku + * 批量创建 SKU * - * @param list sku对象集合 + * @param spuId 商品 SPU 编号 + * @param list SKU 对象集合 */ - void createSkus(List list); + void createSkus(Long spuId, List list); + + /** + * 根据 SPU 编号,批量更新它的 SKU 信息 + * + * @param spuId SPU 编码 + * @param skus SKU 的集合 + */ + void updateProductSkus(Long spuId, List skus); /** * 获得商品 sku 集合 @@ -109,11 +83,4 @@ public interface ProductSkuService { */ void deleteSkuBySpuId(Long spuId); - /** - * 根据 spuId 更新 spu 下的 sku 信息 - * - * @param spuId spu 编码 - * @param skus sku 的集合 - */ - void updateSkus(Long spuId, List skus); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java index 91a60929b..bf8dab778 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java @@ -1,14 +1,17 @@ package cn.iocoder.yudao.module.product.service.sku; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyRespVO; -import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueRespVO; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,11 +22,10 @@ import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.PROPERTY_NOT_EXISTS; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; /** - * 商品sku Service 实现类 + * 商品 SKU Service 实现类 * * @author 芋道源码 */ @@ -37,24 +39,6 @@ public class ProductSkuServiceImpl implements ProductSkuService { @Resource private ProductPropertyService productPropertyService; - @Override - public Long createSku(ProductSkuCreateReqVO createReqVO) { - // 插入 - ProductSkuDO sku = ProductSkuConvert.INSTANCE.convert(createReqVO); - productSkuMapper.insert(sku); - // 返回 - return sku.getId(); - } - - @Override - public void updateSku(ProductSkuUpdateReqVO updateReqVO) { - // 校验存在 - this.validateSkuExists(updateReqVO.getId()); - // 更新 - ProductSkuDO updateObj = ProductSkuConvert.INSTANCE.convert(updateReqVO); - productSkuMapper.updateById(updateObj); - } - @Override public void deleteSku(Long id) { // 校验存在 @@ -80,61 +64,65 @@ public class ProductSkuServiceImpl implements ProductSkuService { } @Override - public PageResult getSkuPage(ProductSkuPageReqVO pageReqVO) { - return productSkuMapper.selectPage(pageReqVO); - } - - @Override - public List getSkuList(ProductSkuExportReqVO exportReqVO) { - return productSkuMapper.selectList(exportReqVO); - } - - // TODO @franky:这个方法,貌似实现的还是有点问题哈。例如说,throw 异常,后面还执行逻辑~ - // TODO @艿艿 咳咳,throw 那里我是偷懒省略了{},哈哈,我加上,然后我调试下,在优化下 - @Override - public void validateSkus(List list) { - List skuPropertyList = list.stream().flatMap(p -> p.getProperties().stream()).collect(Collectors.toList()); - // 校验规格属性以及规格值是否存在 - List propertyIds = skuPropertyList.stream().map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toList()); - List propertyAndValueList = productPropertyService.selectByIds(propertyIds); - if (propertyAndValueList.isEmpty()) { - throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); + public void validateSkus(List skus, Integer specType) { + // 非多规格,不需要校验 + if (ObjectUtil.notEqual(specType, ProductSpuSpecTypeEnum.DISABLE.getType())) { + return; } - Map propertyMap = propertyAndValueList.stream().collect(Collectors.toMap(ProductPropertyRespVO::getId, p -> p)); - skuPropertyList.forEach(p -> { - ProductPropertyRespVO productPropertyRespVO = propertyMap.get(p.getPropertyId()); - // 如果对应的属性名不存在或属性名下的属性值集合为空,给出提示 - if (null == productPropertyRespVO || productPropertyRespVO.getPropertyValueList().isEmpty()) - throw ServiceExceptionUtil.exception(PROPERTY_NOT_EXISTS); - // 判断改属性名对应的属性值是否存在,不存在,给出提示 - if (!productPropertyRespVO.getPropertyValueList().stream().map(ProductPropertyValueRespVO::getId).collect(Collectors.toSet()).contains(p.getValueId())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PROPERTY_VALUE_NOT_EXISTS); + + // 1、校验规格属性存在 + // TODO @Luowenfeng:stream 的写法;不用改哈,就是说下可以酱紫写; + Set propertyIds = skus.stream().filter(p -> p.getProperties() != null).flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性 + .map(ProductSkuBaseVO.Property::getPropertyId).collect(Collectors.toSet()); // 将每个 Property 转换成对应的 propertyId,最后形成集合 + List propertyAndValueList = productPropertyService.getPropertyAndValueList(propertyIds); + if (propertyAndValueList.size() == propertyIds.size()) { + throw exception(PROPERTY_NOT_EXISTS); + } + + // 2. 校验,一个 SKU 下,没有重复的规格。校验方式是,遍历每个 SKU ,看看是否有重复的规格 propertyId + Map propertyValueMap = propertyAndValueList.stream().filter(p -> p.getValues() != null).flatMap(p -> p.getValues().stream()) + .collect(Collectors.toMap(ProductPropertyValueRespVO::getId, value -> value)); // KEY:规格属性值的编号 + skus.forEach(sku -> { + Set skuPropertyIds = CollectionUtils.convertSet(sku.getProperties(), propertyItem -> propertyValueMap.get(propertyItem.getValueId()).getPropertyId()); + if (skuPropertyIds.size() != sku.getProperties().size()) { + throw exception(SKU_PROPERTIES_DUPLICATED); } }); - // 校验是否有重复的sku组合 - List> skuProperties = list.stream().map(ProductSkuBaseVO::getProperties).collect(Collectors.toList()); - Set skuPropertiesConvertSet = new HashSet<>(); - skuProperties.forEach(p -> { - // 组合属性值id为 1~2~3.... 形式的字符串,通过set的特性判断是否有重复的组合 - if (!skuPropertiesConvertSet.add(p.stream().map(pr -> String.valueOf(pr.getValueId())).sorted().collect(Collectors.joining("~")))) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.SKU_PROPERTIES_DUPLICATED); + + // 3. 再校验,每个 Sku 的规格值的数量,是一致的。 + int attrValueIdsSize = skus.get(0).getProperties().size(); + for (int i = 1; i < skus.size(); i++) { + if (attrValueIdsSize != skus.get(i).getProperties().size()) { + throw exception(ErrorCodeConstants.SPU_ATTR_NUMBERS_MUST_BE_EQUALS); } - }); + } + + // 4. 最后校验,每个 Sku 之间不是重复的 + Set> skuAttrValues = new HashSet<>(); // 每个元素,都是一个 Sku 的 attrValueId 集合。这样,通过最外层的 Set ,判断是否有重复的. + for (ProductSkuCreateOrUpdateReqVO sku : skus) { + // TODO @Luowenfeng:可以使用 CollectionUtils.convertSet(),简化下面的 stream 操作 + if (!skuAttrValues.add(sku.getProperties().stream().map(ProductSkuBaseVO.Property::getValueId).collect(Collectors.toSet()))) { // 添加失败,说明重复 + throw exception(ErrorCodeConstants.SPU_SKU_NOT_DUPLICATE); + } + } } @Override - public void createSkus(List skuDOList) { + public void createSkus(Long spuId, List skuCreateReqList) { + // 批量插入 SKU + List skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList); + skuDOList.forEach(v -> v.setSpuId(spuId)); productSkuMapper.insertBatch(skuDOList); } @Override public List getSkusBySpuId(Long spuId) { - return productSkuMapper.selectBySpuIds(Collections.singletonList(spuId)); + return productSkuMapper.selectListBySpuIds(Collections.singletonList(spuId)); } @Override public List getSkusBySpuIds(List spuIds) { - return productSkuMapper.selectBySpuIds(spuIds); + return productSkuMapper.selectListBySpuIds(spuIds); } @Override @@ -144,22 +132,21 @@ public class ProductSkuServiceImpl implements ProductSkuService { @Override @Transactional - public void updateSkus(Long spuId, List skus) { - List allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus); - // 查询 spu 下已经存在的 sku 的集合 - List existsSkus = productSkuMapper.selectBySpuIds(Collections.singletonList(spuId)); - // TODO @franky:使用 CollUtils 即可 - Map existsSkuMap = existsSkus.stream().collect(Collectors.toMap(ProductSkuDO::getId, p -> p)); + public void updateProductSkus(Long spuId, List skus) { + // 查询 SPU 下已经存在的 SKU 的集合 + List existsSkus = productSkuMapper.selectListBySpuId(spuId); + Map existsSkuMap = CollectionUtils.convertMap(existsSkus, ProductSkuDO::getId); - // 拆分三个集合, 新插入的, 需要更新的,需要删除的 + // 拆分三个集合,新插入的、需要更新的、需要删除的 List insertSkus = new ArrayList<>(); - List updateSkus = new ArrayList<>(); + List updateSkus = new ArrayList<>(); // TODO Luowenfeng:使用 Long 即可 List deleteSkus = new ArrayList<>(); - // TODO @芋艿:是不是基于规格匹配会比较好。 + // TODO @Luowenfeng:是不是基于规格匹配会比较好。可以参考下 onemall 的 ProductSpuServiceImpl 的 updateProductSpu 逻辑 + List allUpdateSkus = ProductSkuConvert.INSTANCE.convertSkuDOList(skus); allUpdateSkus.forEach(p -> { - if (null != p.getId()) { - if (existsSkuMap.get(p.getId()) != null) { + if (p.getId() != null) { + if (existsSkuMap.containsKey(p.getId())) { updateSkus.add(p); return; } @@ -170,14 +157,12 @@ public class ProductSkuServiceImpl implements ProductSkuService { insertSkus.add(p); }); - if (insertSkus.size() > 0) { + if (CollectionUtil.isNotEmpty(insertSkus)) { productSkuMapper.insertBatch(insertSkus); } - if (updateSkus.size() > 0) { updateSkus.forEach(p -> productSkuMapper.updateById(p)); } - if (deleteSkus.size() > 0) { productSkuMapper.deleteBatchIds(deleteSkus.stream().map(ProductSkuDO::getId).collect(Collectors.toList())); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java index e4fc47d56..e5affa8d2 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java @@ -1,20 +1,27 @@ package cn.iocoder.yudao.module.product.service.spu; -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; /** - * 商品spu Service 接口 + * 商品 SPU Service 接口 * * @author 芋道源码 */ public interface ProductSpuService { /** - * 创建商品spu + * 创建商品 SPU * * @param createReqVO 创建信息 * @return 编号 @@ -22,11 +29,11 @@ public interface ProductSpuService { Long createSpu(@Valid ProductSpuCreateReqVO createReqVO); /** - * 更新商品spu + * 更新商品 SPU * * @param updateReqVO 更新信息 */ - void updateSpu(@Valid SpuUpdateReqVO updateReqVO); + void updateSpu(@Valid ProductSpuUpdateReqVO updateReqVO); /** * 删除商品spu @@ -41,7 +48,7 @@ public interface ProductSpuService { * @param id 编号 * @return 商品spu */ - SpuRespVO getSpu(Long id); + ProductSpuRespVO getSpu(Long id); /** * 获得商品spu列表 @@ -57,14 +64,15 @@ public interface ProductSpuService { * @param pageReqVO 分页查询 * @return 商品spu分页 */ - PageResult getSpuPage(SpuPageReqVO pageReqVO); + PageResult getSpuPage(ProductSpuPageReqVO pageReqVO); /** - * 获得商品spu列表, 用于 Excel 导出 + * 获得商品spu分页 * - * @param exportReqVO 查询条件 - * @return 商品spu列表 + * @param pageReqVO 分页查询 + * @return 商品spu分页 */ - List getSpuList(SpuExportReqVO exportReqVO); + PageResult getSpuPage(AppSpuPageReqVO pageReqVO); + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java index 4420b0af8..847301ba8 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java @@ -1,16 +1,27 @@ package cn.iocoder.yudao.module.product.service.spu; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.property.ProductPropertyAndValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.ProductPropertyViewRespVO; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuBaseVO; +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppSpuPageRespVO; import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; -import cn.iocoder.yudao.module.product.service.category.CategoryService; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum; +import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; +import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; +import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +35,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; /** - * 商品spu Service 实现类 + * 商品 SPU Service 实现类 * * @author 芋道源码 */ @@ -36,55 +47,70 @@ public class ProductSpuServiceImpl implements ProductSpuService { private ProductSpuMapper ProductSpuMapper; @Resource - private CategoryService categoryService; + private ProductCategoryService categoryService; @Resource private ProductSkuService productSkuService; + @Resource + private ProductPropertyService productPropertyService; + + @Resource + private ProductBrandService brandService; + @Override @Transactional public Long createSpu(ProductSpuCreateReqVO createReqVO) { // 校验分类 - categoryService.validatedCategoryById(createReqVO.getCategoryId()); + categoryService.validateCategoryLevel(createReqVO.getCategoryId()); + // 校验品牌 + brandService.validateProductBrand(createReqVO.getBrandId()); // 校验SKU - List skuCreateReqList = createReqVO.getSkus(); - productSkuService.validateSkus(skuCreateReqList); - // 插入SPU + List skuCreateReqList = createReqVO.getSkus(); + productSkuService.validateSkus(skuCreateReqList, createReqVO.getSpecType()); + // 插入 SPU ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); + spu.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice)); + spu.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + spu.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + spu.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); ProductSpuMapper.insert(spu); - // sku关联SPU属性 - skuCreateReqList.forEach(p -> { - p.setSpuId(spu.getId()); - }); - List skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList); - // 批量插入sku - productSkuService.createSkus(skuDOList); + // 插入 SKU + productSkuService.createSkus(spu.getId(), skuCreateReqList); // 返回 return spu.getId(); } @Override @Transactional - public void updateSpu(SpuUpdateReqVO updateReqVO) { - // 校验 spu 是否存在 - this.validateSpuExists(updateReqVO.getId()); + public void updateSpu(ProductSpuUpdateReqVO updateReqVO) { + // 校验 SPU 是否存在 + validateSpuExists(updateReqVO.getId()); // 校验分类 - categoryService.validatedCategoryById(updateReqVO.getCategoryId()); + categoryService.validateCategoryLevel(updateReqVO.getCategoryId()); + // 校验品牌 + brandService.validateProductBrand(updateReqVO.getBrandId()); // 校验SKU - List skuCreateReqList = updateReqVO.getSkus(); - productSkuService.validateSkus(skuCreateReqList); - // 更新 + List skuCreateReqList = updateReqVO.getSkus(); + // 多规格才需校验 + productSkuService.validateSkus(skuCreateReqList, updateReqVO.getSpecType()); + + // 更新 SPU ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); + updateObj.setMarketPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getMarketPrice)); + updateObj.setMaxPrice(CollectionUtils.getMaxValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + updateObj.setMinPrice(CollectionUtils.getMinValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getPrice)); + updateObj.setTotalStock(CollectionUtils.getSumValue(skuCreateReqList, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); ProductSpuMapper.updateById(updateObj); - // 更新 sku - productSkuService.updateSkus(updateObj.getId(), updateReqVO.getSkus()); + // 批量更新 SKU + productSkuService.updateProductSkus(updateObj.getId(), updateReqVO.getSkus()); } @Override @Transactional public void deleteSpu(Long id) { // 校验存在 - this.validateSpuExists(id); + validateSpuExists(id); // 删除 SPU ProductSpuMapper.deleteById(id); // 删除关联的 SKU @@ -98,12 +124,38 @@ public class ProductSpuServiceImpl implements ProductSpuService { } @Override - public SpuRespVO getSpu(Long id) { + // TODO @芋艿:需要再 review 下 + public ProductSpuRespVO getSpu(Long id) { ProductSpuDO spu = ProductSpuMapper.selectById(id); - SpuRespVO spuVO = ProductSpuConvert.INSTANCE.convert(spu); + ProductSpuRespVO spuVO = ProductSpuConvert.INSTANCE.convert(spu); if (null != spuVO) { List skuReqs = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuId(id)); spuVO.setSkus(skuReqs); + List properties = new ArrayList<>(); + // 组合 sku 规格属性 + if(spu.getSpecType().equals(ProductSpuSpecTypeEnum.DISABLE.getType())) { + for (ProductSkuRespVO productSkuRespVO : skuReqs) { + properties.addAll(productSkuRespVO.getProperties()); + } + Map> propertyMaps = properties.stream().collect(Collectors.groupingBy(ProductSkuBaseVO.Property::getPropertyId)); + List propertyAndValueList = productPropertyService.getPropertyAndValueList(new ArrayList<>(propertyMaps.keySet())); + // 装载组装过后的属性 + List productPropertyViews = new ArrayList<>(); + propertyAndValueList.forEach(p -> { + ProductPropertyViewRespVO productPropertyViewRespVO = new ProductPropertyViewRespVO(); + productPropertyViewRespVO.setPropertyId(p.getId()); + productPropertyViewRespVO.setName(p.getName()); + List propertyValues = new ArrayList<>(); + Map propertyValueMaps = p.getValues().stream().collect(Collectors.toMap(ProductPropertyValueRespVO::getId, pv -> pv)); + propertyMaps.get(p.getId()).forEach(pv -> { + ProductPropertyViewRespVO.Tuple2 tuple2 = new ProductPropertyViewRespVO.Tuple2(pv.getValueId(), propertyValueMaps.get(pv.getValueId()).getName()); + propertyValues.add(tuple2); + }); + productPropertyViewRespVO.setPropertyValues(propertyValues.stream().distinct().collect(Collectors.toList())); + productPropertyViews.add(productPropertyViewRespVO); + }); + spuVO.setProductPropertyViews(productPropertyViews); + } // 组合分类 if (null != spuVO.getCategoryId()) { LinkedList categoryArray = new LinkedList<>(); @@ -127,21 +179,30 @@ public class ProductSpuServiceImpl implements ProductSpuService { } @Override - public PageResult getSpuPage(SpuPageReqVO pageReqVO) { - PageResult spuVOs = ProductSpuConvert.INSTANCE.convertPage(ProductSpuMapper.selectPage(pageReqVO)); + public PageResult getSpuPage(ProductSpuPageReqVO pageReqVO) { + PageResult spuVOs = ProductSpuConvert.INSTANCE.convertPage(ProductSpuMapper.selectPage(pageReqVO)); // 查询 sku 的信息 - List spuIds = spuVOs.getList().stream().map(SpuRespVO::getId).collect(Collectors.toList()); + List spuIds = spuVOs.getList().stream().map(ProductSpuRespVO::getId).collect(Collectors.toList()); List skus = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuIds(spuIds)); // TODO @franky:使用 CollUtil 里的方法替代哈 - Map> skuMap = skus.stream().collect(Collectors.groupingBy(ProductSkuRespVO::getSpuId)); - // 将 spu 和 sku 进行组装 - spuVOs.getList().forEach(p -> p.setSkus(skuMap.get(p.getId()))); + // TODO 芋艿:临时注释 +// Map> skuMap = skus.stream().collect(Collectors.groupingBy(ProductSkuRespVO::getSpuId)); +// // 将 spu 和 sku 进行组装 +// spuVOs.getList().forEach(p -> p.setSkus(skuMap.get(p.getId()))); return spuVOs; } @Override - public List getSpuList(SpuExportReqVO exportReqVO) { - return ProductSpuMapper.selectList(exportReqVO); + public PageResult getSpuPage(AppSpuPageReqVO pageReqVO) { + PageResult productSpuDOPageResult = ProductSpuMapper.selectPage(ProductSpuConvert.INSTANCE.convert(pageReqVO)); + PageResult pageResult = new PageResult<>(); + List collect = productSpuDOPageResult.getList() + .stream() + .map(ProductSpuConvert.INSTANCE::convertAppResp) + .collect(Collectors.toList()); + pageResult.setList(collect); + pageResult.setTotal(productSpuDOPageResult.getTotal()); + return pageResult; } } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImplTest.java deleted file mode 100644 index fc819e6b6..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImplTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package cn.iocoder.yudao.module.product.service.brand; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*; -import cn.iocoder.yudao.module.product.dal.dataobject.brand.BrandDO; -import cn.iocoder.yudao.module.product.dal.mysql.brand.BrandMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** -* {@link BrandServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ -@Import(BrandServiceImpl.class) -public class BrandServiceImplTest extends BaseDbUnitTest { - - @Resource - private BrandServiceImpl brandService; - - @Resource - private BrandMapper brandMapper; - - @Test - public void testCreateBrand_success() { - // 准备参数 - BrandCreateReqVO reqVO = randomPojo(BrandCreateReqVO.class); - - // 调用 - Long brandId = brandService.createBrand(reqVO); - // 断言 - assertNotNull(brandId); - // 校验记录的属性是否正确 - BrandDO brand = brandMapper.selectById(brandId); - assertPojoEquals(reqVO, brand); - } - - @Test - public void testUpdateBrand_success() { - // mock 数据 - BrandDO dbBrand = randomPojo(BrandDO.class); - brandMapper.insert(dbBrand);// @Sql: 先插入出一条存在的数据 - // 准备参数 - BrandUpdateReqVO reqVO = randomPojo(BrandUpdateReqVO.class, o -> { - o.setId(dbBrand.getId()); // 设置更新的 ID - }); - - // 调用 - brandService.updateBrand(reqVO); - // 校验是否更新正确 - BrandDO brand = brandMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, brand); - } - - @Test - public void testUpdateBrand_notExists() { - // 准备参数 - BrandUpdateReqVO reqVO = randomPojo(BrandUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> brandService.updateBrand(reqVO), BRAND_NOT_EXISTS); - } - - @Test - public void testDeleteBrand_success() { - // mock 数据 - BrandDO dbBrand = randomPojo(BrandDO.class); - brandMapper.insert(dbBrand);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbBrand.getId(); - - // 调用 - brandService.deleteBrand(id); - // 校验数据不存在了 - assertNull(brandMapper.selectById(id)); - } - - @Test - public void testDeleteBrand_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> brandService.deleteBrand(id), BRAND_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetBrandPage() { - // mock 数据 - BrandDO dbBrand = randomPojo(BrandDO.class, o -> { // 等会查询到 - o.setCategoryId(null); - o.setName(null); - o.setStatus(null); - o.setCreateTime(null); - }); - brandMapper.insert(dbBrand); - // 测试 categoryId 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setCategoryId(null))); - // 测试 name 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setName(null))); - // 测试 status 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setCreateTime(null))); - // 准备参数 - BrandPageReqVO reqVO = new BrandPageReqVO(); - reqVO.setCategoryId(null); - reqVO.setName(null); - reqVO.setStatus(null); - reqVO.setCreateTime((new Date[]{})); - - // 调用 - PageResult pageResult = brandService.getBrandPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbBrand, pageResult.getList().get(0)); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetBrandList() { - // mock 数据 - BrandDO dbBrand = randomPojo(BrandDO.class, o -> { // 等会查询到 - o.setCategoryId(null); - o.setName(null); - o.setStatus(null); - o.setCreateTime(null); - }); - brandMapper.insert(dbBrand); - // 测试 categoryId 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setCategoryId(null))); - // 测试 name 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setName(null))); - // 测试 status 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setCreateTime(null))); - // 准备参数 - BrandExportReqVO reqVO = new BrandExportReqVO(); - reqVO.setCategoryId(null); - reqVO.setName(null); - reqVO.setStatus(null); - reqVO.setCreateTime((new Date[]{})); - - // 调用 - List list = brandService.getBrandList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbBrand, list.get(0)); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImplTest.java new file mode 100644 index 000000000..93817d44c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/ProductBrandServiceImplTest.java @@ -0,0 +1,132 @@ +package cn.iocoder.yudao.module.product.service.brand; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.brand.vo.ProductBrandUpdateReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO; +import cn.iocoder.yudao.module.product.dal.mysql.brand.ProductBrandMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.util.Date; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.BRAND_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** +* {@link ProductBrandServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(ProductBrandServiceImpl.class) +public class ProductBrandServiceImplTest extends BaseDbUnitTest { + + @Resource + private ProductBrandServiceImpl brandService; + + @Resource + private ProductBrandMapper brandMapper; + + @Test + public void testCreateBrand_success() { + // 准备参数 + ProductBrandCreateReqVO reqVO = randomPojo(ProductBrandCreateReqVO.class); + + // 调用 + Long brandId = brandService.createBrand(reqVO); + // 断言 + assertNotNull(brandId); + // 校验记录的属性是否正确 + ProductBrandDO brand = brandMapper.selectById(brandId); + assertPojoEquals(reqVO, brand); + } + + @Test + public void testUpdateBrand_success() { + // mock 数据 + ProductBrandDO dbBrand = randomPojo(ProductBrandDO.class); + brandMapper.insert(dbBrand);// @Sql: 先插入出一条存在的数据 + // 准备参数 + ProductBrandUpdateReqVO reqVO = randomPojo(ProductBrandUpdateReqVO.class, o -> { + o.setId(dbBrand.getId()); // 设置更新的 ID + }); + + // 调用 + brandService.updateBrand(reqVO); + // 校验是否更新正确 + ProductBrandDO brand = brandMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, brand); + } + + @Test + public void testUpdateBrand_notExists() { + // 准备参数 + ProductBrandUpdateReqVO reqVO = randomPojo(ProductBrandUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> brandService.updateBrand(reqVO), BRAND_NOT_EXISTS); + } + + @Test + public void testDeleteBrand_success() { + // mock 数据 + ProductBrandDO dbBrand = randomPojo(ProductBrandDO.class); + brandMapper.insert(dbBrand);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbBrand.getId(); + + // 调用 + brandService.deleteBrand(id); + // 校验数据不存在了 + assertNull(brandMapper.selectById(id)); + } + + @Test + public void testDeleteBrand_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> brandService.deleteBrand(id), BRAND_NOT_EXISTS); + } + + @Test + public void testGetBrandPage() { + // mock 数据 + ProductBrandDO dbBrand = randomPojo(ProductBrandDO.class, o -> { // 等会查询到 + o.setName("芋道源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2022, 2, 1)); + }); + brandMapper.insert(dbBrand); + // 测试 name 不匹配 + brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setName("源码"))); + // 测试 status 不匹配 + brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + brandMapper.insert(cloneIgnoreId(dbBrand, o -> o.setCreateTime(buildTime(2022, 3, 1)))); + // 准备参数 + ProductBrandPageReqVO reqVO = new ProductBrandPageReqVO(); + reqVO.setName("芋道"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime((new Date[]{buildTime(2022, 1, 1), buildTime(2022, 2, 25)})); + + // 调用 + PageResult pageResult = brandService.getBrandPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbBrand, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImplTest.java deleted file mode 100644 index 34384201b..000000000 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImplTest.java +++ /dev/null @@ -1,192 +0,0 @@ -package cn.iocoder.yudao.module.product.service.category; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryCreateReqVO; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO; -import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryUpdateReqVO; -import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO; -import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link CategoryServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(CategoryServiceImpl.class) -public class CategoryServiceImplTest extends BaseDbUnitTest { - - @Resource - private CategoryServiceImpl categoryService; - - @Resource - private CategoryMapper categoryMapper; - - @Test - public void testCreateCategory_success() { - // 准备参数 - CategoryCreateReqVO reqVO = randomPojo(CategoryCreateReqVO.class); - - // 调用 - Long categoryId = categoryService.createCategory(reqVO); - // 断言 - assertNotNull(categoryId); - // 校验记录的属性是否正确 - CategoryDO category = categoryMapper.selectById(categoryId); - assertPojoEquals(reqVO, category); - } - - @Test - public void testUpdateCategory_success() { - // mock 数据 - CategoryDO dbCategory = randomPojo(CategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - CategoryUpdateReqVO reqVO = randomPojo(CategoryUpdateReqVO.class, o -> { - o.setId(dbCategory.getId()); // 设置更新的 ID - }); - - // 调用 - categoryService.updateCategory(reqVO); - // 校验是否更新正确 - CategoryDO category = categoryMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, category); - } - - @Test - public void testUpdateCategory_notExists() { - // 准备参数 - CategoryUpdateReqVO reqVO = randomPojo(CategoryUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.updateCategory(reqVO), CATEGORY_NOT_EXISTS); - } - - @Test - public void testDeleteCategory_success() { - // mock 数据 - CategoryDO dbCategory = randomPojo(CategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbCategory.getId(); - - // 调用 - categoryService.deleteCategory(id); - // 校验数据不存在了 - assertNull(categoryMapper.selectById(id)); - } - - @Test - public void testDeleteCategory_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetCategoryPage() { - // mock 数据 - CategoryDO dbCategory = randomPojo(CategoryDO.class, o -> { // 等会查询到 - o.setParentId(null); - o.setName(null); - o.setIcon(null); - o.setBannerUrl(null); - o.setSort(null); - o.setDescription(null); - o.setStatus(null); - o.setCreateTime(null); - }); - categoryMapper.insert(dbCategory); - // 测试 pid 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setParentId(null))); - // 测试 name 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); - // 测试 icon 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setIcon(null))); - // 测试 bannerUrl 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setBannerUrl(null))); - // 测试 sort 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setSort(null))); - // 测试 description 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setDescription(null))); - // 测试 status 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null))); - // 准备参数 - CategoryPageReqVO reqVO = new CategoryPageReqVO(); - reqVO.setName(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); - - // 调用 - PageResult pageResult = categoryService.getCategoryPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbCategory, pageResult.getList().get(0)); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetCategoryList() { - // mock 数据 - CategoryDO dbCategory = randomPojo(CategoryDO.class, o -> { // 等会查询到 - o.setParentId(null); - o.setName(null); - o.setIcon(null); - o.setBannerUrl(null); - o.setSort(null); - o.setDescription(null); - o.setStatus(null); - o.setCreateTime(null); - }); - categoryMapper.insert(dbCategory); - // 测试 pid 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setParentId(null))); - // 测试 name 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); - // 测试 icon 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setIcon(null))); - // 测试 bannerUrl 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setBannerUrl(null))); - // 测试 sort 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setSort(null))); - // 测试 description 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setDescription(null))); - // 测试 status 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null))); - // 准备参数 - CategoryExportReqVO reqVO = new CategoryExportReqVO(); - reqVO.setName(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); - - // 调用 - List list = categoryService.getCategoryList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbCategory, list.get(0)); - } - -} diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java new file mode 100644 index 000000000..a56c1aa1a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.product.service.category; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryUpdateReqVO; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; +import cn.iocoder.yudao.module.product.dal.mysql.category.ProductCategoryMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link ProductCategoryServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(ProductCategoryServiceImpl.class) +public class ProductCategoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private ProductCategoryServiceImpl productCategoryService; + + @Resource + private ProductCategoryMapper productCategoryMapper; + + @Test + public void testCreateCategory_success() { + // 准备参数 + ProductCategoryCreateReqVO reqVO = randomPojo(ProductCategoryCreateReqVO.class); + // mock 父类 + ProductCategoryDO parentProductCategory = randomPojo(ProductCategoryDO.class, o -> o.setId(reqVO.getParentId())); + productCategoryMapper.insert(parentProductCategory); + + // 调用 + Long categoryId = productCategoryService.createCategory(reqVO); + // 断言 + assertNotNull(categoryId); + // 校验记录的属性是否正确 + ProductCategoryDO category = productCategoryMapper.selectById(categoryId); + assertPojoEquals(reqVO, category); + } + + @Test + public void testUpdateCategory_success() { + // mock 数据 + ProductCategoryDO dbCategory = randomPojo(ProductCategoryDO.class); + productCategoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + ProductCategoryUpdateReqVO reqVO = randomPojo(ProductCategoryUpdateReqVO.class, o -> { + o.setId(dbCategory.getId()); // 设置更新的 ID + }); + // mock 父类 + ProductCategoryDO parentProductCategory = randomPojo(ProductCategoryDO.class, o -> o.setId(reqVO.getParentId())); + productCategoryMapper.insert(parentProductCategory); + + // 调用 + productCategoryService.updateCategory(reqVO); + // 校验是否更新正确 + ProductCategoryDO category = productCategoryMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, category); + } + + @Test + public void testUpdateCategory_notExists() { + // 准备参数 + ProductCategoryUpdateReqVO reqVO = randomPojo(ProductCategoryUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> productCategoryService.updateCategory(reqVO), CATEGORY_NOT_EXISTS); + } + + @Test + public void testDeleteCategory_success() { + // mock 数据 + ProductCategoryDO dbCategory = randomPojo(ProductCategoryDO.class); + productCategoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCategory.getId(); + + // 调用 + productCategoryService.deleteCategory(id); + // 校验数据不存在了 + assertNull(productCategoryMapper.selectById(id)); + } + + @Test + public void testDeleteCategory_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> productCategoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); + } + + @Test + public void testGetCategoryList() { + // mock 数据 + ProductCategoryDO dbCategory = randomPojo(ProductCategoryDO.class, o -> { // 等会查询到 + o.setName("奥特曼"); + }); + productCategoryMapper.insert(dbCategory); + // 测试 name 不匹配 + productCategoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("奥特块"))); + // 准备参数 + ProductCategoryListReqVO reqVO = new ProductCategoryListReqVO(); + reqVO.setName("特曼"); + + // 调用 + List list = productCategoryService.getEnableCategoryList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbCategory, list.get(0)); + } + +} diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java index 42eb96fd8..10ab8a3d1 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java @@ -1,30 +1,17 @@ package cn.iocoder.yudao.module.product.service.sku; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.product.controller.admin.sku.vo.*; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.mysql.sku.ProductSkuMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link ProductSkuServiceImpl} 的单元测试类 @@ -40,46 +27,6 @@ public class SkuServiceImplTest extends BaseDbUnitTest { @Resource private ProductSkuMapper ProductSkuMapper; - @Test - public void testCreateSku_success() { - // 准备参数 - ProductSkuCreateReqVO reqVO = randomPojo(ProductSkuCreateReqVO.class); - - // 调用 - Long skuId = ProductSkuService.createSku(reqVO); - // 断言 - assertNotNull(skuId); - // 校验记录的属性是否正确 - ProductSkuDO sku = ProductSkuMapper.selectById(skuId); - assertPojoEquals(reqVO, sku); - } - - @Test - public void testUpdateSku_success() { - // mock 数据 - ProductSkuDO dbSku = randomPojo(ProductSkuDO.class); - ProductSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据 - // 准备参数 - ProductSkuUpdateReqVO reqVO = randomPojo(ProductSkuUpdateReqVO.class, o -> { - o.setId(dbSku.getId()); // 设置更新的 ID - }); - - // 调用 - ProductSkuService.updateSku(reqVO); - // 校验是否更新正确 - ProductSkuDO sku = ProductSkuMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, sku); - } - - @Test - public void testUpdateSku_notExists() { - // 准备参数 - ProductSkuUpdateReqVO reqVO = randomPojo(ProductSkuUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> ProductSkuService.updateSku(reqVO), SKU_NOT_EXISTS); - } - @Test public void testDeleteSku_success() { // mock 数据 @@ -103,111 +50,4 @@ public class SkuServiceImplTest extends BaseDbUnitTest { assertServiceException(() -> ProductSkuService.deleteSku(id), SKU_NOT_EXISTS); } - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetSkuPage() { - // mock 数据 - ProductSkuDO dbSku = randomPojo(ProductSkuDO.class, o -> { // 等会查询到 - o.setSpuId(null); - o.setProperties(null); - o.setPrice(null); - o.setOriginalPrice(null); - o.setCostPrice(null); - o.setBarCode(null); - o.setPicUrl(null); - o.setStatus(null); - o.setCreateTime(null); - }); - ProductSkuMapper.insert(dbSku); - // 测试 spuId 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setSpuId(null))); - // 测试 properties 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setProperties(null))); - // 测试 price 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setPrice(null))); - // 测试 originalPrice 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setOriginalPrice(null))); - // 测试 costPrice 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setCostPrice(null))); - // 测试 barCode 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setBarCode(null))); - // 测试 picUrl 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setPicUrl(null))); - // 测试 status 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setCreateTime(null))); - // 准备参数 - ProductSkuPageReqVO reqVO = new ProductSkuPageReqVO(); - reqVO.setSpuId(null); - reqVO.setProperties(null); - reqVO.setPrice(null); - reqVO.setOriginalPrice(null); - reqVO.setCostPrice(null); - reqVO.setBarCode(null); - reqVO.setPicUrl(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); - - // 调用 - PageResult pageResult = ProductSkuService.getSkuPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSku, pageResult.getList().get(0)); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetSkuList() { - // mock 数据 - ProductSkuDO dbSku = randomPojo(ProductSkuDO.class, o -> { // 等会查询到 - o.setSpuId(null); - o.setProperties(null); - o.setPrice(null); - o.setOriginalPrice(null); - o.setCostPrice(null); - o.setBarCode(null); - o.setPicUrl(null); - o.setStatus(null); - o.setCreateTime(null); - }); - ProductSkuMapper.insert(dbSku); - // 测试 spuId 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setSpuId(null))); - // 测试 properties 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setProperties(null))); - // 测试 price 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setPrice(null))); - // 测试 originalPrice 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setOriginalPrice(null))); - // 测试 costPrice 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setCostPrice(null))); - // 测试 barCode 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setBarCode(null))); - // 测试 picUrl 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setPicUrl(null))); - // 测试 status 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - ProductSkuMapper.insert(cloneIgnoreId(dbSku, o -> o.setCreateTime(null))); - // 准备参数 - ProductSkuExportReqVO reqVO = new ProductSkuExportReqVO(); - reqVO.setSpuId(null); - reqVO.setProperties(null); - reqVO.setPrice(null); - reqVO.setOriginalPrice(null); - reqVO.setCostPrice(null); - reqVO.setBarCode(null); - reqVO.setPicUrl(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); - - // 调用 - List list = ProductSkuService.getSkuList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbSku, list.get(0)); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java index fae41788f..bb0fa9d32 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java @@ -1,30 +1,25 @@ package cn.iocoder.yudao.module.product.service.spu; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; /** * {@link ProductSpuServiceImpl} 的单元测试类 @@ -60,7 +55,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class); ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据 // 准备参数 - SpuUpdateReqVO reqVO = randomPojo(SpuUpdateReqVO.class, o -> { + ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class, o -> { o.setId(dbSpu.getId()); // 设置更新的 ID }); @@ -74,7 +69,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { @Test public void testUpdateSpu_notExists() { // 准备参数 - SpuUpdateReqVO reqVO = randomPojo(SpuUpdateReqVO.class); + ProductSpuUpdateReqVO reqVO = randomPojo(ProductSpuUpdateReqVO.class); // 调用, 并断言异常 assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS); @@ -114,9 +109,9 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { o.setCategoryId(null); o.setPicUrls(null); o.setSort(null); - o.setLikeCount(null); - o.setPrice(null); - o.setQuantity(null); +// o.setLikeCount(null); +// o.setPrice(null); +// o.setQuantity(null); o.setStatus(null); o.setCreateTime(null); }); @@ -134,17 +129,17 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { // 测试 sort 不匹配 ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSort(null))); // 测试 likeCount 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setLikeCount(null))); +// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setLikeCount(null))); // 测试 price 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPrice(null))); +// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPrice(null))); // 测试 quantity 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setQuantity(null))); +// ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setQuantity(null))); // 测试 status 不匹配 ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(null))); // 测试 createTime 不匹配 ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(null))); // 准备参数 - SpuPageReqVO reqVO = new SpuPageReqVO(); + ProductSpuPageReqVO reqVO = new ProductSpuPageReqVO(); reqVO.setName(null); reqVO.setSellPoint(null); reqVO.setDescription(null); @@ -158,72 +153,11 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { reqVO.setCreateTime(null); // 调用 - PageResult pageResult = spuService.getSpuPage(reqVO); + PageResult pageResult = spuService.getSpuPage(reqVO); // 断言 assertEquals(1, pageResult.getTotal()); assertEquals(1, pageResult.getList().size()); assertPojoEquals(dbSpu, pageResult.getList().get(0)); } - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetSpuList() { - // mock 数据 - ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class, o -> { // 等会查询到 - o.setName(null); - o.setSellPoint(null); - o.setDescription(null); - o.setCategoryId(null); - o.setPicUrls(null); - o.setSort(null); - o.setLikeCount(null); - o.setPrice(null); - o.setQuantity(null); - o.setStatus(null); - o.setCreateTime(null); - }); - ProductSpuMapper.insert(dbSpu); - // 测试 name 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setName(null))); - // 测试 sellPoint 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSellPoint(null))); - // 测试 description 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setDescription(null))); - // 测试 categoryId 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCategoryId(null))); - // 测试 picUrls 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPicUrls(null))); - // 测试 sort 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setSort(null))); - // 测试 likeCount 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setLikeCount(null))); - // 测试 price 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setPrice(null))); - // 测试 quantity 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setQuantity(null))); - // 测试 status 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(null))); - // 测试 createTime 不匹配 - ProductSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(null))); - // 准备参数 - SpuExportReqVO reqVO = new SpuExportReqVO(); - reqVO.setName(null); - reqVO.setSellPoint(null); - reqVO.setDescription(null); - reqVO.setCategoryId(null); - reqVO.setPicUrls(null); - reqVO.setSort(null); - reqVO.setLikeCount(null); - reqVO.setPrice(null); - reqVO.setQuantity(null); - reqVO.setStatus(null); - reqVO.setCreateTime(null); - - // 调用 - List list = spuService.getSpuList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbSpu, list.get(0)); - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/resources/application-unit-test.yaml b/yudao-module-mall/yudao-module-product-biz/src/test/resources/application-unit-test.yaml new file mode 100644 index 000000000..3a2079cdc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/application-unit-test.yaml @@ -0,0 +1,50 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis-plus: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +# Resilience4j 配置项 + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/resources/logback.xml b/yudao-module-mall/yudao-module-product-biz/src/test/resources/logback.xml new file mode 100644 index 000000000..daf756bff --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql index 94f0b7937..37fc0ff21 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql @@ -2,8 +2,7 @@ CREATE TABLE IF NOT EXISTS "product_category" ( "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, "parent_id" bigint(20) NOT NULL, "name" varchar(255) NOT NULL, - "icon" varchar(100), - "banner_url" varchar(255) NOT NULL, + "pic_url" varchar(255) NOT NULL, "sort" int(11) NOT NULL, "description" varchar(1024) NOT NULL, "status" tinyint(4) NOT NULL, @@ -12,15 +11,13 @@ CREATE TABLE IF NOT EXISTS "product_category" ( "updater" varchar(64) DEFAULT '', "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint(20) NOT NULL, PRIMARY KEY ("id") ) COMMENT '商品分类'; CREATE TABLE IF NOT EXISTS "product_brand" ( "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "category_id" bigint(20) NOT NULL, "name" varchar(255) NOT NULL, - "banner_url" varchar(255) NOT NULL, + "pic_url" varchar(255) NOT NULL, "sort" int(11), "description" varchar(1024), "status" tinyint(4) NOT NULL, @@ -29,6 +26,5 @@ CREATE TABLE IF NOT EXISTS "product_brand" ( "updater" varchar(64) DEFAULT '', "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint(20) NOT NULL, PRIMARY KEY ("id") -) COMMENT '品牌'; +) COMMENT '商品品牌'; diff --git a/yudao-module-mall/yudao-module-trade-api/pom.xml b/yudao-module-mall/yudao-module-trade-api/pom.xml new file mode 100644 index 000000000..1299ad11d --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/pom.xml @@ -0,0 +1,26 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + 4.0.0 + yudao-module-trade-api + jar + + ${project.artifactId} + + trade 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..5bdc0ff82 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.enums.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * Trade-Order 错误码 Core 枚举类 + *

+ * Trade-Order 系统,使用 1-011-000-000 段 + * + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface ErrorCodeConstants { + + /** + * ========== Order 模块 1-011-000-000 ========== + */ + ErrorCode ORDER_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品不存在"); + ErrorCode ORDER_SPU_NOT_SALE = new ErrorCode(1011000002, "商品不可售卖"); + ErrorCode ORDER_SKU_NOT_SALE = new ErrorCode(1011000003, "商品Sku不可售卖"); + ErrorCode ORDER_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品库存不足"); + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java new file mode 100644 index 000000000..670651d4e --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.trade.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易订单 - 关闭类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderCancelTypeEnum { + + PAY_TIMEOUT(10, "超时未支付"), + REFUND_CLOSE(20, "退款关闭"), + MEMBER_CANCEL(30, "买家取消"), + PAY_ON_DELIVERY(40, "已通过货到付款交易"),; + + /** + * 关闭类型 + */ + private final Integer type; + /** + * 关闭类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderItemRefundStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderItemRefundStatusEnum.java new file mode 100644 index 000000000..4d81ea9eb --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderItemRefundStatusEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.trade.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易订单项 - 退款状态 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderItemRefundStatusEnum { + + NONE(0, "未申请退款"), + APPLY(1, "申请退款"), + WAIT(2, "等待退款"), + SUCCESS(3, "退款成功"); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java new file mode 100644 index 000000000..50fd88b12 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.trade.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易订单 - 退款状态 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderRefundStatusEnum { + + NONE(0, "未退款"), + PART(1, "部分退款"), + ALL(2, "全部退款"); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java new file mode 100644 index 000000000..fe0a85f3d --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.trade.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易订单 - 状态 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderStatusEnum { + + WAITING_PAYMENT(0, "待付款"), + WAIT_SHIPMENT(1, "待发货"), + ALREADY_SHIPMENT(2, "待收货"), + COMPLETED(3, "已完成"), + CANCEL(4, "已关闭"); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java new file mode 100644 index 000000000..c10d0065a --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.trade.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易订单 - 类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderTypeEnum { + + NORMAL(0, "普通订单"), + SECKILL(1, "秒杀订单"), + TEAM(2, "拼团订单"), + BARGAIN(3, "砍价订单"); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/refund/TradeRefundTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/refund/TradeRefundTypeEnum.java new file mode 100644 index 000000000..f74b39bba --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/refund/TradeRefundTypeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.trade.enums.refund; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 交易退款 - 申请类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeRefundTypeEnum { + + REFUND(10, "退款"), + RETURN_AND_REFUND(20, "退货退款"); + + /** + * 状态值 + */ + private final Integer type; + /** + * 状态名 + */ + private final String name; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/pom.xml b/yudao-module-mall/yudao-module-trade-biz/pom.xml new file mode 100644 index 000000000..c9b582d93 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/pom.xml @@ -0,0 +1,78 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + 4.0.0 + yudao-module-trade-biz + jar + + ${project.artifactId} + + trade 模块,主要实现交易相关功能 + 例如:订单、退款、购物车等功能。 + + + + + cn.iocoder.boot + yudao-module-trade-api + ${revision} + + + + cn.iocoder.boot + yudao-module-product-api + ${revision} + + + + cn.iocoder.boot + yudao-module-pay-api + ${revision} + + + + cn.iocoder.boot + yudao-module-market-api + ${revision} + + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-operatelog + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + yudao-spring-boot-starter-web + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + + + + diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java new file mode 100644 index 000000000..91e51dfd5 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart; + +import io.swagger.annotations.Api; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "购物车 API") +@RestController +@RequestMapping("/cart") +@Validated +public class CartController { + +// @Autowired +// private CartManager cartManager; +// +// @PostMapping("add") +// @ApiOperation("添加商品到购物车") +// @ApiImplicitParams({ +// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"), +// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024") +// }) +// @RequiresAuthenticate +// public CommonResult addCartItem(@RequestParam("skuId") Integer skuId, +// @RequestParam("quantity") Integer quantity) { +// cartManager.addCartItem(UserSecurityContextHolder.getUserId(), skuId, quantity); +// return success(true); +// } +// +// @GetMapping("sum-quantity") +// @ApiOperation("查询用户在购物车中的商品数量") +// @RequiresAuthenticate +// public CommonResult sumCartItemQuantity() { +// return success(cartManager.sumCartItemQuantity(UserSecurityContextHolder.getUserId())); +// } +// +// @GetMapping("/get-detail") +// @ApiOperation("查询用户的购物车的商品列表") +// @RequiresAuthenticate +// public CommonResult getCartDetail() { +// return success(cartManager.getCartDetail(UserSecurityContextHolder.getUserId())); +// } +// +// @PostMapping("update-quantity") +// @ApiOperation("更新购物车商品数量") +// @ApiImplicitParams({ +// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"), +// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024") +// }) +// @RequiresAuthenticate +// public CommonResult updateCartItemQuantity(@RequestParam("skuId") Integer skuId, +// @RequestParam("quantity") Integer quantity) { +// cartManager.updateCartItemQuantity(UserSecurityContextHolder.getUserId(), skuId, quantity); +// return success(true); +// } +// +// @PostMapping("update-selected") +// @ApiOperation("更新购物车商品是否选中") +// @ApiImplicitParams({ +// @ApiImplicitParam(name = "skuIds", value = "商品 SKU 编号数组", required = true, example = "1,3"), +// @ApiImplicitParam(name = "selected", value = "是否选中", required = true, example = "true") +// }) +// @RequiresAuthenticate +// public CommonResult updateCartItemSelected(@RequestParam("skuIds") Set skuIds, +// @RequestParam("selected") Boolean selected) { +// cartManager.updateCartItemSelected(UserSecurityContextHolder.getUserId(), skuIds, selected); +// // 获得目前购物车明细 +// return success(true); +// } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java new file mode 100644 index 000000000..403efbebe --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java @@ -0,0 +1,211 @@ +package cn.iocoder.yudao.module.trade.controller.app.cart.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿:swagger 文档完善 +@Data +@Accessors(chain = true) +public class CartDetailVO { + + /** + * 商品分组数组 + */ + private List itemGroups; + /** + * 费用 + */ + private Fee fee; + + /** + * 商品分组 + * + * 多个商品,参加同一个活动,从而形成分组。 + */ + @Data + @Accessors(chain = true) + public static class ItemGroup { + +// /** +// * 优惠活动 +// */ +// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 + /** + * 促销减少的金额 + * + * 1. 若未参与促销活动,或不满足促销条件,返回 null + * 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。 + */ + private Integer activityDiscountTotal; + /** + * 商品数组 + */ + private List items; + + } + + @Data + @Accessors(chain = true) + public static class Sku { + + // SKU 自带信息 + /** + * sku 编号 + */ + private Integer id; + /** + * SPU 信息 + */ + private Spu spu; + /** + * 图片地址 + */ + private String picURL; +// /** +// * 规格值数组 +// */ +// private List attrs; // TODO 后面改下 + /** + * 价格,单位:分 + */ + private Integer price; + /** + * 库存数量 + */ + private Integer quantity; + + // 非 SKU 自带信息 + + /** + * 购买数量 + */ + private Integer buyQuantity; + /** + * 是否选中 + */ + private Boolean selected; +// /** +// * 优惠活动 +// */ +// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 + /** + * 原始单价,单位:分。 + */ + private Integer originPrice; + /** + * 购买单价,单位:分 + */ + private Integer buyPrice; + /** + * 最终价格,单位:分。 + */ + private Integer presentPrice; + /** + * 购买总金额,单位:分 + * + * 用途类似 {@link #presentTotal} + */ + private Integer buyTotal; + /** + * 优惠总金额,单位:分。 + */ + private Integer discountTotal; + /** + * 最终总金额,单位:分。 + * + * 注意,presentPrice * quantity 不一定等于 presentTotal 。 + * 因为,存在无法整除的情况。 + * 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。 + * 所以,需要存储一个该字段。 + */ + private Integer presentTotal; + + } + + @Data + @Accessors(chain = true) + public static class Spu { + + /** + * SPU 编号 + */ + private Integer id; + + // ========== 基本信息 ========= + /** + * SPU 名字 + */ + private String name; + /** + * 分类编号 + */ + private Integer cid; + /** + * 商品主图地址 + * + * 数组,以逗号分隔 + * + * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张 + */ + private List picUrls; + + } + + /** + * 费用(合计) + */ + @Data + @Accessors(chain = true) + public static class Fee { + + /** + * 购买总价 + */ + private Integer buyTotal; + /** + * 优惠总价 + * + * 注意,满多少元包邮,不算在优惠中。 + */ + private Integer discountTotal; + /** + * 邮费 + */ + private Integer postageTotal; + /** + * 最终价格 + * + * 计算公式 = 总价 - 优惠总价 + 邮费 + */ + private Integer presentTotal; + + public Fee() { + } + + public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) { + this.buyTotal = buyTotal; + this.discountTotal = discountTotal; + this.postageTotal = postageTotal; + this.presentTotal = presentTotal; + } + + } + + /** + * 邮费信息 TODO 芋艿,未完成 + */ + @Data + @Accessors(chain = true) + public static class Postage { + + /** + * 需要满足多少钱,可以包邮。单位:分 + */ + private Integer threshold; + + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http new file mode 100644 index 000000000..778e99029 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http @@ -0,0 +1,31 @@ +### /trade-order/confirm-create-order-info 基于商品,确认创建订单 +GET {{appApi}}/trade/order/get-create-info?items[0].skuId=1&items[0].count=1 +Authorization: Bearer {{user-access-token}} +tenant-id: {{appTenentId}} + +### /trade-order/confirm-create-order-info-from-cart 基于购物车,确认创建订单 +GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{user-access-token}} + +### /trade-order/confirm-create-order-info-from-cart 基于商品,创建订单 +POST {{shop-api-base-url}}/trade-order/create +Content-Type: application/json +Authorization: Bearer {{user-access-token}} + +{ + "userAddressId": 19, + "remark": "我是备注", + "orderItems": [ + { + "skuId": 3, + "quantity": 1 + } + ] +} + +### /trade-order/page 获得订单交易分页 +GET {{shop-api-base-url}}/trade-order/page?status=1&pageNo=1&pageSize=10 +Content-Type: application/x-www-form-urlencoded + +### diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java new file mode 100644 index 000000000..7b7ed4b83 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.trade.controller.app.order; + +import cn.hutool.extra.servlet.ServletUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderGetCreateInfoRespVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderPageReqVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.TradeOrderRespVO; +import cn.iocoder.yudao.module.trade.service.order.TradeOrderService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +@Api(tags = "用户 App - 交易订单") +@RestController +@RequestMapping("/trade/order") +@RequiredArgsConstructor +@Validated +@Slf4j +public class AppTradeOrderController { + + // TODO 在思考下; + + private final TradeOrderService tradeOrderService; + + + @GetMapping("/get-create-info") + @ApiOperation("基于商品,确认创建订单") + @PreAuthenticated + public CommonResult getTradeOrderCreateInfo(AppTradeOrderCreateReqVO createReqVO) { +// return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId)); + return null; + } + + @PostMapping("/create") + @ApiOperation("创建订单") + @PreAuthenticated + public CommonResult createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO, + HttpServletRequest servletRequest) { +// return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(), +// HttpUtil.getIp(servletRequest), createReqVO)); + // 获取登录用户 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + // 获取用户ip地址 + String clientIp = ServletUtil.getClientIP(servletRequest); + // 创建交易订单,预支付记录 + Long result = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO); + + return CommonResult.success(result); + } + + @GetMapping("/get") + @ApiOperation("获得交易订单") + @ApiImplicitParam(name = "tradeOrderId", value = "交易订单编号", required = true) + public CommonResult getTradeOrder(@RequestParam("tradeOrderId") Integer tradeOrderId) { +// return success(tradeOrderService.getTradeOrder(tradeOrderId)); + return null; + } + + @GetMapping("/page") + @ApiOperation("获得订单交易分页") + public CommonResult> pageTradeOrder(TradeOrderPageReqVO pageVO) { +// return success(tradeOrderService.pageTradeOrder(UserSecurityContextHolder.getUserId(), pageVO)); + return null; + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java new file mode 100644 index 000000000..54e856a6b --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.trade.controller.app.order.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel(value = "用户 App - 交易订单创建 Request VO") +@Data +public class AppTradeOrderCreateReqVO { + + @ApiModelProperty(name = "收件地址编号", required = true, example = "1") + @NotNull(message = "收件地址不能为空") + private Integer addressId; + + @ApiModelProperty(name = "优惠劵编号", example = "1024") + private Long couponId; + + @ApiModelProperty(name = "备注", example = "1024") + private String remark; + + @ApiModelProperty(name = "是否来自购物车", required = true, example = "true", notes = "true - 来自购物车;false - 立即购买") + @NotNull(message = "是否来自购物车不能为空") + private Boolean fromCart; + + /** + * 订单商品项列表 + */ + @NotNull(message = "必须选择购买的商品") + private List items; + + @ApiModel(value = "订单商品项") + @Data + public static class Item { + + @ApiModelProperty(name = "商品 SKU 编号", required = true, example = "111") + @NotNull(message = "商品 SKU 编号不能为空") + private Long skuId; + + @ApiModelProperty(name = "商品 SKU 购买数量", required = true, example = "1024") + @NotNull(message = "商品 SKU 购买数量不能为空") + @Min(value = 1, message = "商品 SKU 购买数量必须大于 0") + private Integer count; + + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderGetCreateInfoRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderGetCreateInfoRespVO.java new file mode 100644 index 000000000..d0bf595f6 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderGetCreateInfoRespVO.java @@ -0,0 +1,169 @@ +package cn.iocoder.yudao.module.trade.controller.app.order.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@ApiModel(value = "用户 App - 订单获得创建信息 Response VO") +@Data +public class AppTradeOrderGetCreateInfoRespVO { + + /** + * 商品分组数组 + */ + private List itemGroups; + /** + * 费用 + */ + private Fee fee; + +// /** +// * 优惠劵列表 TODO 芋艿,后续改改 +// */ +// private List coupons; + + @ApiModel(value = "商品分组", description = "多个商品,参加同一个活动,从而形成分组") + @Data + public static class ItemGroup { + +// /** +// * 优惠活动 +// */ +// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 + /** + * 商品 SKU 数组 + */ + private List items; + + } + + @ApiModel("商品 SKU") + @Data + public static class Sku { + + // SKU 自带信息 + @ApiModelProperty(value = "SKU 编号", required = true, example = "1024") + private Integer id; + /** + * SPU 信息 + */ + private Spu spu; + /** + * 图片地址 + */ + private String picURL; +// /** +// * 规格值数组 +// */ +// private List attrs; // TODO 后面改下 + /** + * 价格,单位:分 + */ + private Integer price; + /** + * 库存数量 + */ + private Integer stock; + + // 非 SKU 自带信息 + + /** + * 购买数量 + */ + private Integer buyQuantity; +// /** +// * 优惠活动 +// */ +// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒 + /** + * 原始单价,单位:分。 + */ + private Integer originPrice; + /** + * 购买单价,单位:分 + */ + private Integer buyPrice; + /** + * 最终价格,单位:分。 + */ + private Integer presentPrice; + /** + * 购买总金额,单位:分 + * + * 用途类似 {@link #presentTotal} + */ + private Integer buyTotal; + /** + * 优惠总金额,单位:分。 + */ + private Integer discountTotal; + /** + * 最终总金额,单位:分。 + * + * 注意,presentPrice * quantity 不一定等于 presentTotal 。 + * 因为,存在无法整除的情况。 + * 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。 + * 所以,需要存储一个该字段。 + */ + private Integer presentTotal; + + } + + @Data + public static class Spu { + + /** + * SPU 编号 + */ + private Integer id; + + // ========== 基本信息 ========= + /** + * SPU 名字 + */ + private String name; + /** + * 分类编号 + */ + private Integer cid; + /** + * 商品主图地址 + * + * 数组,以逗号分隔 + * + * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张 + */ + private List picUrls; + + } + + @ApiModel("费用(合计)") + @Data + @AllArgsConstructor + public static class Fee { + + @ApiModelProperty(value = "购买总价", required = true, example = "1024") + private Integer buyPrice; + /** + * 优惠总价 + * + * 注意,满多少元包邮,不算在优惠中。 + */ + private Integer discountTotal; + /** + * 邮费 + */ + private Integer postageTotal; + /** + * 最终价格 + * + * 计算公式 = 总价 - 优惠总价 + 邮费 + */ + private Integer presentTotal; + + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderItemRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderItemRespVO.java new file mode 100644 index 000000000..195f7330f --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderItemRespVO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.trade.controller.app.order.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +@ApiModel("交易订单项 Response VO") +@Data +public class TradeOrderItemRespVO { + + @ApiModelProperty(value = "id自增长", required = true) + private Integer id; + @ApiModelProperty(value = "订单编号", required = true) + private Integer orderId; + @ApiModelProperty(value = "订单项状态", required = true) + private Integer status; + @ApiModelProperty(value = "商品 SKU 编号", required = true) + private Integer skuId; + @ApiModelProperty(value = "商品 SPU 编号", required = true) + private Integer spuId; + @ApiModelProperty(value = "商品名字", required = true) + private String skuName; + @ApiModelProperty(value = "图片名字", required = true) + private String skuImage; + @ApiModelProperty(value = "商品数量", required = true) + private Integer quantity; + @ApiModelProperty(value = "原始单价,单位:分", required = true) + private Integer originPrice; + @ApiModelProperty(value = "购买单价,单位:分", required = true) + private Integer buyPrice; + @ApiModelProperty(value = "最终价格,单位:分", required = true) + private Integer presentPrice; + @ApiModelProperty(value = "购买总金额,单位:分", required = true) + private Integer buyTotal; + @ApiModelProperty(value = "优惠总金额,单位:分", required = true) + private Integer discountTotal; + @ApiModelProperty(value = "最终总金额,单位:分", required = true) + private Integer presentTotal; + @ApiModelProperty(value = "退款总金额,单位:分", required = true) + private Integer refundTotal; + @ApiModelProperty(value = "物流id") + private Integer logisticsId; + @ApiModelProperty(value = "售后状态", required = true) + private Integer afterSaleStatus; + @ApiModelProperty(value = "售后订单编号") + private Integer afterSaleOrderId; + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderPageReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderPageReqVO.java new file mode 100644 index 000000000..5c8b6c872 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderPageReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.trade.controller.app.order.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@ApiModel("交易订单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class TradeOrderPageReqVO extends PageParam { + + @ApiModelProperty(value = "订单状态", example = "1", notes = "参见 TradeOrderStatusEnum 枚举") + private Integer orderStatus; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderRespVO.java new file mode 100644 index 000000000..4b7ffa77c --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/TradeOrderRespVO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.trade.controller.app.order.vo; + +import lombok.*; +import io.swagger.annotations.*; +import java.util.*; + +@ApiModel("订单交易 Response VO") +@Data +public class TradeOrderRespVO { + + @ApiModelProperty(value = "订单编号", required = true) + private Integer id; + @ApiModelProperty(value = "用户编号", required = true) + private Integer userId; + @ApiModelProperty(value = "订单单号", required = true) + private String orderNo; + @ApiModelProperty(value = "订单状态", required = true) + private Integer orderStatus; + @ApiModelProperty(value = "备注") + private String remark; + @ApiModelProperty(value = "订单结束时间") + private Date endTime; + @ApiModelProperty(value = "订单金额(总金额),单位:分", required = true) + private Integer buyPrice; + @ApiModelProperty(value = "优惠总金额,单位:分", required = true) + private Integer discountPrice; + @ApiModelProperty(value = "物流金额,单位:分", required = true) + private Integer logisticsPrice; + @ApiModelProperty(value = "最终金额,单位:分", required = true) + private Integer presentPrice; + @ApiModelProperty(value = "支付金额,单位:分", required = true) + private Integer payPrice; + @ApiModelProperty(value = "退款金额,单位:分", required = true) + private Integer refundPrice; + @ApiModelProperty(value = "付款时间") + private Date payTime; + @ApiModelProperty(value = "支付订单编号") + private Integer payTransactionId; + @ApiModelProperty(value = "支付渠道") + private Integer payChannel; + @ApiModelProperty(value = "配送类型", required = true) + private Integer deliveryType; + @ApiModelProperty(value = "发货时间") + private Date deliveryTime; + @ApiModelProperty(value = "收货时间") + private Date receiveTime; + @ApiModelProperty(value = "收件人名称", required = true) + private String receiverName; + @ApiModelProperty(value = "手机号", required = true) + private String receiverMobile; + @ApiModelProperty(value = "地区编码", required = true) + private Integer receiverAreaCode; + @ApiModelProperty(value = "收件详细地址", required = true) + private String receiverDetailAddress; + @ApiModelProperty(value = "售后状态", required = true) + private Integer afterSaleStatus; + @ApiModelProperty(value = "优惠劵编号") + private Integer couponCardId; + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 订单项数组 + * + * // TODO 芋艿,后续考虑怎么优化下,目前是内嵌了别的 dto + */ + private List orderItems; + + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/refund/TradeRefundController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/refund/TradeRefundController.java new file mode 100644 index 000000000..aa419190e --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/refund/TradeRefundController.java @@ -0,0 +1,4 @@ +package cn.iocoder.yudao.module.trade.controller.app.refund; + +public class TradeRefundController { +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/package-info.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/package-info.java new file mode 100644 index 000000000..aa2f99f35 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package cn.iocoder.yudao.module.trade.controller; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java new file mode 100644 index 000000000..1b81212b8 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.trade.convert.order; + +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface TradeOrderConvert { + + TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); + + @Mapping(source = "order.couponId", target = "couponId") + TradeOrderDO convert(AppTradeOrderCreateReqVO createReqVO, PriceCalculateRespDTO.Order order); +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java new file mode 100644 index 000000000..23113f9f3 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.trade.convert.order; + +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface TradeOrderItemConvert { + + TradeOrderItemConvert INSTANCE = Mappers.getMapper(TradeOrderItemConvert.class); + + /** + * + * @param tradeOrder 交易订单 + * @param items sku列表价格 + * @return 订单项 + */ + @Mappings({ + @Mapping(source = "tradeOrder.userId", target = "userId"), + @Mapping(source = "tradeOrder.orderId", target = "orderId") + }) + default List convertList(TradeOrderDO tradeOrder, List items) { + // TODO @Com: Mapstruct 生成会报错 + throw new UnsupportedOperationException("无法实现"); + } +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java new file mode 100644 index 000000000..22dd11fe9 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.trade.convert.pay; + +import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import org.mapstruct.factory.Mappers; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface PayOrderConvert { + + PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class); + + + PayOrderDataCreateReqDTO convert(TradeOrderDO tradeOrderDO); +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java new file mode 100644 index 000000000..3b2d173c3 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/price/PriceConvert.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.convert.price; + +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface PriceConvert { + + PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class); + + @Mappings( + @Mapping(source = "userId" , target = "userId") + ) + PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO , Long userId); +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java new file mode 100644 index 000000000..fffc6fa42 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.trade.convert.sku; + +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface ProductSkuConvert { + + ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class); + + List convert(List tradeOrderItems); +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java new file mode 100644 index 000000000..b92ca8b54 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.trade.dal.dataobject.cart; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 购物车的商品信息 DO + */ +@TableName("trade_cart_item") +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class CartItemDO extends BaseDO { + + // ========= 基础字段 BEGIN ========= + + /** + * 编号,唯一自增 + */ + private Long id; + /** + * 是否选中 + */ + private Boolean selected; + /** + * 购物时间 + */ + private Date buyTime; // basket_date + + // ========= 基础字段 END ========= + + // ========= 买家信息 BEGIN ========= + + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + + // ========= 买家信息 END ========= + + // ========= 商品信息 BEGIN ========= + + /** + * 商品 SPU 编号 + * + * 关联 ProductSpuDO 的 id 编号 + */ + private Long spuId; + /** + * 商品 SKU 编号 + * + * 关联 ProductSkuDO 的 id 编号 + */ + private Long skuId; + /** + * 商品购买数量 + */ + private Integer stock; + + // ========= 商品信息 END ========= + + // ========= 优惠信息 BEGIN ========= + +// /** +// * 商品营销活动编号 +// */ +// private Long activityId; // discount_id +// /** +// * 商品营销活动类型 +// */ +// private Integer activityType; + // TODO 芋艿:combination_id 拼团 ID + // TODO 芋艿:seckill_id 秒杀产品 ID + // TODO 芋艿:bargain_id 砍价 ID + + // ========= 优惠信息 END ========= + + // TODO 待确定字段:mf + // TODO 芋艿:distribution_card_no 推广员 + // TODO 芋艿:is_pay 未购买、已购买 + // TODO 芋艿:is_new 是否立即购买 + + // TODO 待确定字段: yv + // TODO isPay: 是否购买 + // TODO isNew:是否立即购买 + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java new file mode 100644 index 000000000..77bdf66b0 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java @@ -0,0 +1,304 @@ +package cn.iocoder.yudao.module.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.product.enums.delivery.DeliveryTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.util.Date; + +/** + * 交易订单 DO + * + * @author 芋道源码 + */ +@TableName("trade_order") +@KeySequence("trade_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TradeOrderDO extends BaseDO { + + // ========== 订单基本信息 ========== + /** + * 订单编号,主键自增 + */ + private Long id; + /** + * 订单流水号 + * + * 例如说,1146347329394184195 + */ + private String sn; + /** + * 订单类型 + * + * 枚举 {@link TradeOrderTypeEnum} + */ + private Integer type; // TODO order_promotion_type + /** + * 订单来源终端 + * + * 枚举 {@link TerminalEnum} + */ + private Integer terminal; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 用户 IP + */ + private String userIp; + /** + * 用户备注 + */ + private String userRemark; + /** + * 订单状态 + * + * 枚举 {@link TradeOrderStatusEnum} + */ + private Integer status; + // TODO 芋艿:要不要存储 prod_name 购买的商品名门? + /** + * 购买的商品数量 + */ + private Integer productCount; // total_num + /** + * 订单完成时间 + */ + private Date finishTime; + /** + * 订单取消时间 + */ + private Date cancelTime; + /** + * 取消类型 + * + * 枚举 {@link TradeOrderCancelTypeEnum} + */ + private Integer cancelType; + /** + * 商家备注 + */ + private String remark; + + // ========== 价格 + 支付基本信息 ========== + /** + * 是否已支付 + * + * true - 已经支付过 + * false - 没有支付过 + */ + private Boolean payed; + /** + * 付款时间 + */ + private Date payTime; + + // ========== 价格 + 支付基本信息 ========== + // 价格文档 - 淘宝:https://open.taobao.com/docV3.htm?docId=108471&docType=1 + // 价格文档 - 京东到家:https://openo2o.jddj.com/api/getApiDetail/182/4d1494c5e7ac4679bfdaaed950c5bc7f.htm + // 价格文档 - 有赞:https://doc.youzanyun.com/detail/API/0/906 + +// TODO promotion_details(订单优惠信息明细,商品和订单级优惠一般都在里面) + + /** + * 商品原价(总),单位:分 + * + * 基于 {@link TradeOrderItemDO#getTotalOriginalPrice()} 求和 + */ + // niu - goods_money; + private Integer skuOriginalPrice; + /** + * 商品优惠(总),单位:分 + * + * 基于 {@link TradeOrderItemDO#getTotalPromotionPrice()} 求和 + */ + private Integer skuPromotionPrice; + /** + * 订单优惠(总),单位:分 + * + * 例如说:满减折扣;不包括优惠劵、商品优惠 + */ + // niu - promotion_money;taobao - discount_fee(主订单优惠) + private Integer orderPromotionPrice; + /** + * 运费金额,单位:分 + */ + // niu - delivery_money;taobao - post_fee(订单邮费) + private Integer deliveryPrice; + // TODO 芋艿:taobao 的:trade.adjust_fee/order.adjust_fee(调整金额,如:卖家手动修改订单价格,官方数据修复等等) + /** + * 应付金额(总),单位:分 + * + * = {@link #skuOriginalPrice} + * + {@link #deliveryPrice} + * - {@link #skuPromotionPrice} + * - {@link #orderPromotionPrice} + */ + // niu - pay_money;taobao - payment(主订单实付金额) | trade.total_fee(主订单应付金额,参考使用); +// * - {@link #couponPrice} // TODO 芋艿:靠营销表记录 + private Integer payPrice; + /** + * 支付订单编号 + * + * 对接 pay-module-biz 支付服务的支付订单编号,即 PayOrderDO 的 id 编号 + */ + private Long payOrderId; + /** + * 支付成功的支付渠道 + * + * 对应 PayChannelEnum 枚举 + */ + private Integer payChannel; + + // ========== 收件 + 物流基本信息 ========== + /** + * 配送方式 + * 会员用户下单时,选择的配送方式 + * + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer deliveryType; + /** + * 实际的配送方式 + * 管理后台发货时,选择的配送方式 + * + * 0 - 无需物流 + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer actualDeliveryType; // like - shipping_status; + /** + * 配置模板的编号 + * + * 关联 DeliveryTemplateDO 的 id 编号 + */ + private Long deliveryTemplateId; // dvy_id + /** + * 物流公司单号 + */ + private String expressNo; // dvy_flow_id + /** + * 发货状态 + * + * true - 已发货 + * false - 未发货 + */ + private Boolean deliveryStatus; + /** + * 发货时间 + */ + private Date deliveryTime; + /** + * 收货时间 + */ + private Date receiveTime; + /** + * 收件人名称 + */ + private String receiverName; + /** + * 收件人手机 + */ + private String receiverMobile; + /** + * 收件人地区编号 + */ + private Integer receiverAreaId; + /** + * 收件人邮编 + */ + private Integer receiverPostCode; + /** + * 收件人详细地址 + */ + private String receiverDetailAddress; + + // ========== 退款基本信息 ========== + /** + * 退款状态 + * + * 枚举 {@link TradeOrderRefundStatusEnum} + */ + private Integer refundStatus; + /** + * 退款金额,单位:分 + * + * 注意,退款并不会影响 {@link #payPrice} 实际支付金额 + * 也就说,一个订单最终产生多少金额的收入 = payPrice - refundPrice + */ + private Integer refundPrice; + + // ========== 营销基本信息 ========== + /** + * 优惠劵编号 + */ + private Long couponId; +// /** +// * 优惠劵减免金额,单位:分 // TODO 芋艿:靠营销表记录 +// */ +// // niu - coupon_money; +// private Integer couponPrice; +// /** +// * 积分抵扣的金额,单位:分 +// */ +// private Integer integralPrice; +// /** +// * 使用的积分 +// */ +// private Integer useIntegral; + + // TODO ========== 待定字段:yv ========= + // TODO cart_id:购物车 id + // TODO refund_reason_wap_img:退款图片 + // TODO refund_reason_wap_explain:退款用户说明 + // TODO refund_reason_time:退款时间 + // TODO refund_reason_wap:前台退款原因 + // TODO refund_reason:不退款的理由 + + // TODO gain_integral:消费赚取积分 + // TODO back_integral:给用户退了多少积分 + + // TODO combination_id:拼团产品id + // TODO pink_id:拼团id + // TODO seckill_id:秒杀产品ID + // TODO bargain_id:砍价id + + // TODO cost:成本价 + // TODO verify_code:核销码 + // TODO store_id:门店id + + // TODO ========== 待定字段:cf ========= + // TODO before_pay_price:改价前支付金额 + // TODO is_alter_price:是否改价 + // TODO out_trade_no:商户系统内部的订单号 String + + // TODO ========== 待定字段:lf ========= + // TODO settle_id:未结算 + // TODO settle_amount:结算金额 + // TODO team_found_id: 拼团id + // TODO team_id: 拼团活动id + // TODO delivery_id: 发货单ID + // TODO attach_values: 附带的值(赠送时机,赠送积分成长值什么的)Json格式 + + // TODO ========== 待定字段:nf ========= + // TODO delivery_code:整体提货编码 + + // TODO ========== 待定字段:niu ========= + // TODO adjust_money '订单调整金额' + // TODO balance_money ''余额支付金额'' + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java new file mode 100644 index 000000000..14e4de0f4 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -0,0 +1,214 @@ +package cn.iocoder.yudao.module.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 交易订单项 DO + * + * @author 芋道源码 + */ +@TableName(value = "trade_order_item") +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class TradeOrderItemDO extends BaseDO { + + // ========== 订单项基本信息 ========== + /** + * 编号 + */ + private Long id; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 订单编号 + * + * 关联 {@link TradeOrderDO#getId()} + */ + private Long orderId; + + // ========== 商品基本信息 ========== + /** + * 商品 SPU 编号 + * + * 关联 ProductSkuDO 的 spuId 编号 + */ + private Long spuId; + /** + * 商品 SKU 编号 + * + * 关联 ProductSkuDO 的 id 编号 + */ + private Long skuId; + /** + * 规格值数组,JSON 格式 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List properties; + /** + * 商品名称 + */ + private String name; + /** + * 商品图片 + */ + private String picUrl; + /** + * 购买数量 + */ + private Integer count; + /** + * 是否评论 + * + * false - 未评论 + * true - 已评论 + */ + private Boolean commented; + + // ========== 价格 + 支付基本信息 ========== + /** + * 商品原价(单),单位:分 + * + * 对应 ProductSkuDO 的 price 字段 + */ + // like - original_price;niu - costPrice + private Integer originalPrice; + /** + * 商品原价(总),单位:分 + * + * = {@link #originalPrice} * {@link #count} + */ + // like - total_price;niu - 暂无 + private Integer totalOriginalPrice; + /** + * 商品级优惠(总),单位:分 + * + * 例如说“限时折扣”:商品原价的 8 折;商品原价的减 50 元 + */ + // taobao - order.discount_fee(子订单商品优惠) + private Integer totalPromotionPrice; + /** + * 最终购买金额(单),单位:分。 + * + * = {@link #totalPresentPrice} / {@link #count} + */ + private Integer presentPrice; + /** + * 最终购买金额(总),单位:分。 + * + * = {@link #totalOriginalPrice} + * - {@link #totalPromotionPrice} + */ + // like - total_pay_price;niu - goods_money; taobao - order.payment(子订单实付金额,不算主订单分摊金额) | order.total_fee(子订单应付金额,参考使用) + private Integer totalPresentPrice; + // TODO 芋艿:part_mjz_discount(子订单分摊金额);本质上,totalOriginalPrice - totalPayPrice + /** + * 应付金额(总),单位:分 + */ + // taobao - divide_order_fee (分摊后子订单实付金额); + private Integer totalPayPrice; + + // ========== 营销基本信息 ========== +// /** +// * 积分抵扣的金额,单位:分 +// */ +// private Integer integralTotal; // like - integral_price;niu - point_money +// /** +// * 使用的积分 +// */ +// private Integer useIntegral; // niu - use_point + + // ========== 退款基本信息 ========== + /** + * 退款状态 + * + * 枚举 {@link TradeOrderItemRefundStatusEnum} + */ + private Integer refundStatus; // TODO 芋艿:可以考虑去查 + // 如上字段,举个例子: + // 假设购买三个,即 stock = 3 。 + // originPrice = 15 + // 使用限时折扣(单品优惠)8 折,buyPrice = 12 + // 开始算总的价格 + // buyTotal = buyPrice * stock = 12 * 3 = 36 + // discountTotal ,假设有满减送(分组优惠)满 20 减 10 ,并且使用优惠劵满 1.01 减 1 ,则 discountTotal = 10 + 1 = 11 + // presentTotal = buyTotal - discountTotal = 24 - 11 = 13 + // 最终 presentPrice = presentTotal / stock = 13 / 3 = 4.33 + /** + * 退款总金额,单位:分 + */ + private Integer refundTotal; + + /** + * 商品属性 + */ + @Data + public static class Property { + + /** + * 属性编号 + * + * 关联 ProductPropertyDO 的 id 编号 + */ + private Long propertyId; + /** + * 属性值编号 + * + * 关联 ProductPropertyValueDO 的 id 编号 + */ + private Long valueId; + + } + + // TODO 芋艿:basket_date 加入购物车时间; + // TODO 芋艿:distribution_card_no 推广员使用的推销卡号 + + // TODO 待确定:mf + // TODO give_integral:赠送积分 + // TODO is_reply:是否评价,0-未评价,1-已评价 + // TODO is_sub:是否单独分佣,0-否,1-是 + // TODO vip_price:会员价 + // TODO product_type:商品类型:0-普通,1-秒杀,2-砍价,3-拼团,4-视频号 + + // TODO 待确定:lf + // TODO integral_price:积分抵扣的金额 + // TODO member_price:会员价格 + // TODO is_member:是否为会员折扣;0-不是;1-是 + // TODO member_discount:会员折扣(百分比) + + // TODO goods_info 商品信息 + + // TODO integral_price:积分抵扣的金额 + + // TODO 待确定:niu + // TODO is_virtual '是否是虚拟商品' + // TODO goods_class '商品种类(1.实物 2.虚拟3.卡券)' + // TODO adjust_money ''调整金额'' + + // TODO is_fenxiao 是否分销, + // TODO adjust_money 是否分销, + + // TODO delivery_status '配送状态' + // TODO delivery_no ''配送单号'' + // TODO gift_flag '赠品标识' + // TODO gift_flag '赠品标识' + + // TODO refund_status '退款状态' + // TODO refund_type '退款状态' + // TODO 一堆退款字段 + +} + diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/refund/TradeRefundDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/refund/TradeRefundDO.java new file mode 100644 index 000000000..cd246a297 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/refund/TradeRefundDO.java @@ -0,0 +1,148 @@ +package cn.iocoder.yudao.module.trade.dal.dataobject.refund; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.refund.TradeRefundTypeEnum; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; +import java.util.List; + +/** + * 交易退款,用于处理 {@link TradeOrderDO} 交易订单的退货换流程 + */ +// TODO 芋艿:需要调整下每个字段的命名;未完全实现; +@TableName(value = "trade_refund") +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class TradeRefundDO extends BaseDO { + + /** + * 交易退款编号,主键自增 + */ + @Deprecated + private Long id; + /** + * 退款流水号 + * + * 例如说,1146347329394184195 + */ + private String sn; + /** + * 退款状态 + * + * 枚举 {@link TradeOrderRefundStatusEnum} + */ + private Integer status; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 用户手机 + */ + private String userMobile; + /** + * 申请类型 + * + * 枚举 {@link TradeRefundTypeEnum} + */ + private Integer type; + /** + * 用户售后说明 + */ + private String reasonMemo; // buyer_msg + /** + * 用户售后凭证图片的地址数组 + * + * 数组,以逗号分隔 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List reasonPicUrls; // photo_files + + // ========== 商家相关 ========== + + /** + * 商家处理时间 + */ + private Date handleTime; // handel_time + /** + * 商家拒绝理由 + */ + private String rejectReasonMemo; // seller_msg + + // ========== 交易订单相关 ========== + /** + * 交易订单编号 + * + * 外键 {@link TradeOrderDO#getId()} + */ + private Long tradeOrderId; + /** + * 交易订单项编号 + * + * 关联 {@link TradeOrderItemDO#getId()} + * 如果全部退款,则该值设置为 0 即可 + */ + private Long tradeOrderItemId; + /** + * 商品 SKU 编号 + */ + @Deprecated + private Integer skuId; + /** + * 退货商品数量 + */ + private Integer stock; // goods_num + + // ========== 退款相关 ========== + /** + * 退款金额,单位:分。 + */ + private Integer refundPrice; // refund_amount + /** + * 支付退款编号 + * + * 对接 pay-module-biz 支付服务的退款订单编号,即 PayRefundDO 的 id 编号 + */ + private Long payRefundId; + // TODO 芋艿:看看是否有必要冗余,order_number、order_amount、flow_trade_no、out_refund_no、pay_type、return_money_sts、refund_time + + // ========== 退货相关 ========== + /** + * 退货物流公司编号 + * + * 关联 ExpressDO 的 id 编号 + */ + private Long returnExpressId; // express_name + /** + * 退货物流单号 + */ + private String returnExpressNo; // express_no + /** + * 退货时间 + */ + private Date returnDate; // ship_time + + // ========== 收获相关 ========== + + /** + * 收获备注 + */ + private String receiveMemo; // receive_message + /** + * 收货时间 + */ + private Date receiveDate; // receive_time + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java new file mode 100644 index 000000000..3b963540b --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.trade.dal.mysql.order; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface TradeOrderMapper extends BaseMapperX { +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java new file mode 100644 index 000000000..987d5a3f5 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/orderitem/TradeOrderItemMapper.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.trade.dal.mysql.orderitem; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Mapper +public interface TradeOrderItemMapper extends BaseMapperX { +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/package-info.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/package-info.java new file mode 100644 index 000000000..37e0ba7d6 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 占位 + */ +package cn.iocoder.yudao.module.trade.dal.mysql; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/package-info.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/package-info.java new file mode 100644 index 000000000..393abea6a --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/package-info.java @@ -0,0 +1,8 @@ +/** + * product 模块,product 模块,主要实现商品相关功能 + * 例如:品牌、商品分类、spu、sku等功能。 + * + * 1. Controller URL:以 /product/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 product_ 开头,方便在数据库中区分 + */ +package cn.iocoder.yudao.module.trade; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java new file mode 100644 index 000000000..f3fc4b5e1 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.trade.service.order; + +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface TradeOrderService { + + /** + * 创建交易订单 + * @param loginUserId 登录用户 + * @param clientIp 用户ip地址 + * @param createReqVO 创建交易订单请求模型 + * @return 交易订单创建结果 + */ + Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO); +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java new file mode 100644 index 000000000..3554e7d4c --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.trade.service.order; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.market.api.price.PriceApi; +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item; +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderItemConvert; +import cn.iocoder.yudao.module.trade.convert.pay.PayOrderConvert; +import cn.iocoder.yudao.module.trade.convert.price.PriceConvert; +import cn.iocoder.yudao.module.trade.convert.sku.ProductSkuConvert; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; +import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper; +import cn.iocoder.yudao.module.trade.enums.enums.ErrorCodeConstants; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +@Service +@RequiredArgsConstructor +public class TradeOrderServiceImpl implements TradeOrderService { + + private final TradeOrderMapper tradeOrderMapper; + + private final TradeOrderItemMapper tradeOrderItemMapper; + + private final PriceApi priceApi; + + private final ProductSkuApi productSkuApi; + + private final ProductSpuApi productSpuApi; + + private final PayOrderApi payOrderApi; + + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) { + + List items = createReqVO.getItems(); + // 商品SKU检查 sku可售状态,库存 + List skuInfos = productSkuApi.getSkusByIds(CollectionUtils.convertSet(items, Item::getSkuId)); + Map skuInfoMap = CollectionUtils.convertMap(skuInfos, SkuInfoRespDTO::getId); + checkSaleableAndStockFromSpu(skuInfoMap, items); + + // 商品SPU检查 sku可售状态,库存 + List spuInfos = productSpuApi.getSpusByIds(CollectionUtils.convertSet(skuInfos, SkuInfoRespDTO::getSpuId)); + checkSaleableFromSpu(spuInfos); + + // 价格计算 + PriceCalculateReqDTO priceCalculateReqDTO = PriceConvert.INSTANCE.convert(createReqVO, loginUserId); + PriceCalculateRespDTO priceResp = priceApi.calculatePrice(priceCalculateReqDTO); + + // 订单信息记录 + TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(createReqVO, priceResp.getOrder()); + tradeOrderMapper.insert(tradeOrderDO); + + // 订单项信息记录 + List tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getItems()); + //-填充订单项-SKU信息 + fillItemsInfoFromSku(tradeOrderItems, skuInfoMap); + tradeOrderItemMapper.insertBatch(tradeOrderItems); + + // 库存扣减 + List skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems); + productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems)); + // 生成预支付 + + PayOrderDataCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); + return payOrderApi.createPayOrder(payOrderCreateReqDTO); + } + + private void fillItemsInfoFromSku(List tradeOrderItems, + Map spuInfos) { + for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { + // 填充SKU信息 + SkuInfoRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId()); + tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId()); + tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl()); + tradeOrderItem.setName(skuInfoRespDTO.getName()); + // todo + List property = + BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class); + tradeOrderItem.setProperties(property); + } + } + + private void checkSaleableFromSpu(List spuInfos) { + SpuInfoRespDTO spu = CollectionUtils.findFirst(spuInfos, + spuInfoDTO -> !Objects.equals(ProductSpuStatusEnum.ENABLE.getStyle(), spuInfoDTO.getStatus())); + if (Objects.isNull(spu)) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SPU_NOT_SALE); + } + } + + private void checkSaleableAndStockFromSpu(Map skuInfoMap, + List items) { + // sku 不存在 + if (items.size() != skuInfoMap.size()) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND); + } + for (Item item : items) { + SkuInfoRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId()); + // sku禁用 + if (!Objects.equals(CommonStatusEnum.ENABLE.getStatus(), skuInfoDTO.getStatus())) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_SALE); + } + // sku库存不足 + if (item.getCount() > skuInfoDTO.getStock()) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_STOCK_NOT_ENOUGH); + } + } + + } +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java index 2fbb61c8b..1e77db1c8 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java @@ -22,6 +22,5 @@ public interface ErrorCodeConstants { // ========== 用户收件地址 1004004000 ========== ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在"); - ErrorCode ADDRESS_FORBIDDEN = new ErrorCode(1004004001, "没有该操作权限"); } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http index 7f943448a..6bae7c7eb 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http @@ -2,53 +2,53 @@ POST {{appApi}}//member/address/create Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6 +Authorization: Bearer {{appToken}} { - "userId": "245", "name": "yunai", "mobile": "15601691300", - "areaCode": "610632", + "areaId": "610632", + "postCode": "200000", "detailAddress": "芋道源码 233 号 666 室", - "type": "1" + "defaulted": true } ### 请求 /update 接口 => 成功 PUT {{appApi}}//member/address/update Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6 +Authorization: Bearer {{appToken}} { "id": "1", - "userId": "245", "name": "yunai888", "mobile": "15601691300", - "areaCode": "610632", + "areaId": "610632", + "postCode": "200000", "detailAddress": "芋道源码 233 号 666 室", - "type": "1" + "defaulted": false } ### 请求 /delete 接口 => 成功 DELETE {{appApi}}//member/address/delete?id=2 Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 +Authorization: Bearer {{appToken}} ### 请求 /get 接口 => 成功 GET {{appApi}}//member/address/get?id=1 Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 +Authorization: Bearer {{appToken}} ### 请求 /get-default 接口 => 成功 GET {{appApi}}//member/address/get-default Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 +Authorization: Bearer {{appToken}} ### 请求 /list 接口 => 成功 GET {{appApi}}//member/address/list Content-Type: application/json tenant-id: {{appTenentId}} -Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 \ No newline at end of file +Authorization: Bearer {{appToken}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java index 81231a881..8cfbac7d2 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.member.controller.app.address.vo; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.member.enums.AddressTypeEnum; -import lombok.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; /** * 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用 @@ -21,17 +21,20 @@ public class AppAddressBaseVO { @NotNull(message = "手机号不能为空") private String mobile; - @ApiModelProperty(value = "地区编码", required = true) - @NotNull(message = "地区编码不能为空") - private Integer areaCode; + @ApiModelProperty(value = "地区编号", required = true) + @NotNull(message = "地区编号不能为空") + private Long areaId; + + @ApiModelProperty(value = "邮编", required = true) + @NotEmpty(message = "邮编不能为空") + private String postCode; @ApiModelProperty(value = "收件详细地址", required = true) @NotNull(message = "收件详细地址不能为空") private String detailAddress; - @ApiModelProperty(value = "地址类型", required = true) - @NotNull(message = "地址类型不能为空") - @InEnum(AddressTypeEnum.class) - private Integer type; + @ApiModelProperty(value = "是否默认地址", required = true) + @NotNull(message = "是否默认地址不能为空") + private Boolean defaulted; } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java index 546df00c6..467432460 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.member.dal.dataobject.address; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.member.enums.AddressTypeEnum; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; @@ -38,18 +37,22 @@ public class AddressDO extends BaseDO { */ private String mobile; /** - * 地区编码 + * 地区编号 */ - private Integer areaCode; + private Long areaId; + /** + * 邮编 + */ + private String postCode; /** * 收件详细地址 */ private String detailAddress; /** - * 地址类型 + * 是否默认 * - * 枚举 {@link AddressTypeEnum} + * true - 默认收件地址 */ - private Integer type; + private Boolean defaulted; } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java index 40391bfe3..a0bf946fe 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java @@ -69,4 +69,10 @@ public class MemberUserDO extends TenantBaseDO { */ private Date loginDate; + // TODO 芋艿:name 真实名字; + // TODO 芋艿:email 邮箱; + // TODO 芋艿:gender 性别; + // TODO 芋艿:score 积分; + // TODO 芋艿:payPassword 支付密码; + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java index fc02e5879..80f78d41f 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java @@ -1,64 +1,22 @@ package cn.iocoder.yudao.module.member.dal.mysql.address; - import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; -import cn.iocoder.yudao.module.member.enums.AddressTypeEnum; import org.apache.ibatis.annotations.Mapper; import java.util.List; -/** - * 用户收件地址 Mapper - * - * @author 芋道源码 - */ @Mapper public interface AddressMapper extends BaseMapperX { - /** - * 获取当前地址 根据id和userId - * @param userId - * @param id - * @return - */ - default AddressDO getAddressByIdAndUserId(Long userId, Long id) { - QueryWrapperX queryWrapperX = new QueryWrapperX<>(); - queryWrapperX.eq("user_id", userId).eq("id", id); - return selectList(queryWrapperX).stream().findFirst().orElse(null); + default AddressDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(AddressDO::getId, id, AddressDO::getUserId, userId); } - /** - * 获取地址列表 - * @param userId - * @param type - * @return - */ - default List selectListByUserIdAndType(Long userId, Integer type) { - QueryWrapperX queryWrapperX = new QueryWrapperX().eq("user_id", userId) - .eqIfPresent("type", type); - return selectList(queryWrapperX); - } - - /** - * 获取默认地址 - * @param userId - * @return - */ - default AddressDO getDefaultUserAddress(Long userId) { - List addressDOList = selectListByUserIdAndType(userId, AddressTypeEnum.DEFAULT.getType()); - return addressDOList.stream().findFirst().orElse(null); - } - - /** - * 获取默认地址 - * @param id - * @param addressTypeEnum - * @return - */ - default int updateTypeById(Long id, AddressTypeEnum addressTypeEnum) { - return updateById(new AddressDO().setId(id).setType(addressTypeEnum.getType())); + default List selectListByUserIdAndDefaulted(Long userId, Boolean defaulted) { + return selectList(new LambdaQueryWrapperX().eq(AddressDO::getUserId, userId) + .eqIfPresent(AddressDO::getDefaulted, defaulted)); } } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java deleted file mode 100644 index 59465e495..000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.member.enums; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 用户收件地址的类型枚举 - */ -@Getter -@AllArgsConstructor -public enum AddressTypeEnum implements IntArrayValuable { - - DEFAULT(1, "默认收件地址"), - NORMAL(2, "普通收件地址"), // 即非默认收件地址 - - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AddressTypeEnum::getType).toArray(); - - private final Integer type; - private final String desc; - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/package-info.java deleted file mode 100644 index 9e136937a..000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package cn.iocoder.yudao.module.member.enums; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java index e45b075fe..456a3d8ac 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java @@ -1,10 +1,11 @@ package cn.iocoder.yudao.module.member.service.address; -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.member.controller.app.address.vo.*; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.validation.Valid; +import java.util.List; /** * 用户收件地址 Service 接口 @@ -55,5 +56,12 @@ public interface AddressService { */ List getAddressList(Long userId); + /** + * 获得用户默认的收件地址 + * + * @param userId 用户编号 + * @return 用户收件地址 + */ AddressDO getDefaultUserAddress(Long userId); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java index a9cd91d59..bbcbbe592 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java @@ -1,23 +1,20 @@ package cn.iocoder.yudao.module.member.service.address; -import cn.iocoder.yudao.module.member.enums.AddressTypeEnum; -import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; +import cn.iocoder.yudao.module.member.convert.address.AddressConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; +import cn.iocoder.yudao.module.member.dal.mysql.address.AddressMapper; import org.springframework.stereotype.Service; - -import javax.annotation.Resource; - import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import java.util.*; -import cn.iocoder.yudao.module.member.controller.app.address.vo.*; -import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; - -import cn.iocoder.yudao.module.member.convert.address.AddressConvert; -import cn.iocoder.yudao.module.member.dal.mysql.address.AddressMapper; +import javax.annotation.Resource; +import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS; /** * 用户收件地址 Service 实现类 @@ -35,18 +32,11 @@ public class AddressServiceImpl implements AddressService { @Transactional(rollbackFor = Exception.class) public Long createAddress(Long userId, AppAddressCreateReqVO createReqVO) { // 如果添加的是默认收件地址,则将原默认地址修改为非默认 - if (AddressTypeEnum.DEFAULT.getType().equals(createReqVO.getType())) { - //查询到一个,然后进行 update - List addressDOs = selectListByUserIdAndType(userId, AddressTypeEnum.DEFAULT.getType()); - AddressDO defaultUserAddress = addressMapper.getDefaultUserAddress(userId); - if (!CollectionUtils.isEmpty(addressDOs)) { - addressDOs.forEach(userAddressDO -> addressMapper.updateById(new AddressDO() - .setId(userAddressDO.getId()).setType(AddressTypeEnum.NORMAL.getType()))); - } - Optional.ofNullable(defaultUserAddress) - //更新为非默认 - .ifPresent( u -> addressMapper.updateTypeById(u.getId(), AddressTypeEnum.NORMAL)); + if (Boolean.TRUE.equals(createReqVO.getDefaulted())) { + List addresses = addressMapper.selectListByUserIdAndDefaulted(userId, true); + addresses.forEach(address -> addressMapper.updateById(new AddressDO().setId(address.getId()).setDefaulted(false))); } + // 插入 AddressDO address = AddressConvert.INSTANCE.convert(createReqVO); address.setUserId(userId); @@ -59,17 +49,15 @@ public class AddressServiceImpl implements AddressService { @Transactional(rollbackFor = Exception.class) public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) { // 校验存在,校验是否能够操作 - check(userId, updateReqVO.getId()); + validAddressExists(userId, updateReqVO.getId()); + // 如果修改的是默认收件地址,则将原默认地址修改为非默认 - if (AddressTypeEnum.DEFAULT.getType().equals(updateReqVO.getType())) { - //获取默认地址 - AddressDO defaultUserAddress = addressMapper.getDefaultUserAddress(userId); - Optional.ofNullable(defaultUserAddress) - //排除当前地址 - .filter(u -> !u.getId().equals(updateReqVO.getId())) - //更新为非默认 - .ifPresent( u -> addressMapper.updateTypeById(u.getId(), AddressTypeEnum.NORMAL)); + if (Boolean.TRUE.equals(updateReqVO.getDefaulted())) { + List addresses = addressMapper.selectListByUserIdAndDefaulted(userId, true); + addresses.stream().filter(u -> !u.getId().equals(updateReqVO.getId())) // 排除自己 + .forEach(address -> addressMapper.updateById(new AddressDO().setId(address.getId()).setDefaulted(false))); } + // 更新 AddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO); addressMapper.updateById(updateObj); @@ -78,56 +66,32 @@ public class AddressServiceImpl implements AddressService { @Override public void deleteAddress(Long userId, Long id) { // 校验存在,校验是否能够操作 - check(userId, id); + validAddressExists(userId, id); // 删除 addressMapper.deleteById(id); } - /** - * 校验用户收件地址是不是属于该用户 - * - * @param userId 用户编号 - * @param userAddressId 用户收件地址 - */ - private void check(Long userId, Long userAddressId) { - AddressDO addressDO = getAddress(userId, userAddressId); - if(null == addressDO){ + private void validAddressExists(Long userId, Long id) { + AddressDO addressDO = getAddress(userId, id); + if (addressDO == null) { throw exception(ADDRESS_NOT_EXISTS); } - if (!addressDO.getUserId().equals(userId)) { - throw exception(ADDRESS_FORBIDDEN); - } } @Override public AddressDO getAddress(Long userId, Long id) { - return addressMapper.getAddressByIdAndUserId(userId, id); + return addressMapper.selectByIdAndUserId(id, userId); } @Override public List getAddressList(Long userId) { - return selectListByUserIdAndType(userId, null); + return addressMapper.selectListByUserIdAndDefaulted(userId, null); } - /** - * 获取默认地址 - * @param userId - * @return - */ @Override public AddressDO getDefaultUserAddress(Long userId) { - return addressMapper.getDefaultUserAddress(userId); + List addresses = addressMapper.selectListByUserIdAndDefaulted(userId, true); + return CollUtil.getFirst(addresses); } - /** - * 根据类型获取地址列表 - * @param userId - * @param type null则查询全部 - * @return - */ - public List selectListByUserIdAndType(Long userId, Integer type) { - return addressMapper.selectListByUserIdAndType(userId, type); - } - - } diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java index eefa2e13e..6ddf80a0e 100644 --- a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java @@ -5,20 +5,18 @@ import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreate import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; import cn.iocoder.yudao.module.member.dal.mysql.address.AddressMapper; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; /** * {@link AddressServiceImpl} 的单元测试类 @@ -40,7 +38,7 @@ public class AddressServiceImplTest extends BaseDbUnitTest { AppAddressCreateReqVO reqVO = randomPojo(AppAddressCreateReqVO.class); // 调用 - Long addressId = addressService.createAddress(getLoginUserId(), reqVO); + Long addressId = addressService.createAddress(randomLongId(), reqVO); // 断言 assertNotNull(addressId); // 校验记录的属性是否正确 @@ -59,7 +57,7 @@ public class AddressServiceImplTest extends BaseDbUnitTest { }); // 调用 - addressService.updateAddress(getLoginUserId(), reqVO); + addressService.updateAddress(dbAddress.getUserId(), reqVO); // 校验是否更新正确 AddressDO address = addressMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, address); @@ -71,7 +69,7 @@ public class AddressServiceImplTest extends BaseDbUnitTest { AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class); // 调用, 并断言异常 - assertServiceException(() -> addressService.updateAddress(getLoginUserId(), reqVO), ADDRESS_NOT_EXISTS); + assertServiceException(() -> addressService.updateAddress(randomLongId(), reqVO), ADDRESS_NOT_EXISTS); } @Test @@ -83,7 +81,7 @@ public class AddressServiceImplTest extends BaseDbUnitTest { Long id = dbAddress.getId(); // 调用 - addressService.deleteAddress(getLoginUserId(), id); + addressService.deleteAddress(dbAddress.getUserId(), id); // 校验数据不存在了 assertNull(addressMapper.selectById(id)); } @@ -94,67 +92,7 @@ public class AddressServiceImplTest extends BaseDbUnitTest { Long id = randomLongId(); // 调用, 并断言异常 - assertServiceException(() -> addressService.deleteAddress(getLoginUserId(), id), ADDRESS_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void ins() { - // mock 数据 - AddressDO dbAddress = randomPojo(AddressDO.class, o -> { // 等会查询到 - o.setUserId(null); - o.setName(null); - o.setMobile(null); - o.setAreaCode(null); - o.setDetailAddress(null); - o.setType(null); - o.setCreateTime(null); - }); - addressMapper.insert(dbAddress); - // 测试 userId 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setUserId(null))); - // 测试 name 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setName(null))); - // 测试 mobile 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setMobile(null))); - // 测试 areaCode 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setAreaCode(null))); - // 测试 detailAddress 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setDetailAddress(null))); - // 测试 type 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setType(null))); - // 测试 createTime 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setCreateTime(null))); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetAddressList() { - // mock 数据 - AddressDO dbAddress = randomPojo(AddressDO.class, o -> { // 等会查询到 - o.setUserId(null); - o.setName(null); - o.setMobile(null); - o.setAreaCode(null); - o.setDetailAddress(null); - o.setType(null); - o.setCreateTime(null); - }); - addressMapper.insert(dbAddress); - // 测试 userId 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setUserId(null))); - // 测试 name 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setName(null))); - // 测试 mobile 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setMobile(null))); - // 测试 areaCode 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setAreaCode(null))); - // 测试 detailAddress 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setDetailAddress(null))); - // 测试 type 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setType(null))); - // 测试 createTime 不匹配 - addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setCreateTime(null))); + assertServiceException(() -> addressService.deleteAddress(randomLongId(), id), ADDRESS_NOT_EXISTS); } } diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql index 608a3bddf..b0347b058 100644 --- a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/create_tables.sql @@ -35,15 +35,15 @@ CREATE TABLE IF NOT EXISTS "member_address" ( "user_id" bigint(20) NOT NULL, "name" varchar(10) NOT NULL, "mobile" varchar(20) NOT NULL, - "area_code" int(11) NOT NULL, + "area_id" bigint(20) NOT NULL, + "post_code" varchar(16) NOT NULL, "detail_address" varchar(250) NOT NULL, - "type" tinyint(4) NOT NULL, + "defaulted" bit NOT NULL, "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, "creator" varchar(64) DEFAULT '', "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, "updater" varchar(64) DEFAULT '', - "tenant_id" bigint(20) NOT NULL, PRIMARY KEY ("id") - ) COMMENT '用户收件地址'; +) COMMENT '用户收件地址'; diff --git a/yudao-module-pay/yudao-module-pay-api/pom.xml b/yudao-module-pay/yudao-module-pay-api/pom.xml index a7bebc2f1..0903abed5 100644 --- a/yudao-module-pay/yudao-module-pay-api/pom.xml +++ b/yudao-module-pay/yudao-module-pay-api/pom.xml @@ -21,6 +21,13 @@ cn.iocoder.boot yudao-common + + + + org.springframework.boot + spring-boot-starter-validation + true + diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java new file mode 100644 index 000000000..204ce7a32 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.pay.api.order; + +import javax.validation.Valid; + +/** + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface PayOrderApi { + + + /** + * 创建支付单 + * + * @param reqDTO 创建请求 + * @return 支付单编号 + */ + Long createPayOrder(@Valid PayOrderDataCreateReqDTO reqDTO); + +} diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java new file mode 100644 index 000000000..acdfa7a20 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderDataCreateReqDTO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.pay.api.order; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + +/** + * 支付单创建 Request DTO + * @author LeeYan9 + */ +@Data +public class PayOrderDataCreateReqDTO implements Serializable { + + /** + * 应用编号 + */ + @NotNull(message = "应用编号不能为空") + private Long appId; + /** + * 用户 IP + */ + @NotEmpty(message = "用户 IP 不能为空") + private String userIp; + + // ========== 商户相关字段 ========== + + /** + * 商户订单编号 + */ + @NotEmpty(message = "商户订单编号不能为空") + private String merchantOrderId; + /** + * 商品标题 + */ + @NotEmpty(message = "商品标题不能为空") + @Length(max = 32, message = "商品标题不能超过 32") + private String subject; + /** + * 商品描述 + */ + @NotEmpty(message = "商品描述信息不能为空") + @Length(max = 128, message = "商品描述信息长度不能超过128") + private String body; + + // ========== 订单相关字段 ========== + + /** + * 支付金额,单位:分 + */ + @NotNull(message = "支付金额不能为空") + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + private Integer amount; + + /** + * 支付过期时间 + */ + @NotNull(message = "支付过期时间不能为空") + private Date expireTime; + +} \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java new file mode 100644 index 000000000..ff902be92 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/PayOrderApiImpl.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.pay.api; + +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO; +import org.springframework.stereotype.Service; + +/** + * TODO 注释 + */ +@Service +public class PayOrderApiImpl implements PayOrderApi { + + @Override + public Long createPayOrder(PayOrderDataCreateReqDTO reqDTO) { + return null; + } + +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index 2f39519a3..97bb86262 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -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, "手机号不存在"); diff --git a/yudao-module-system/yudao-module-system-biz/pom.xml b/yudao-module-system/yudao-module-system-biz/pom.xml index 4dbb0973c..ecac34aa5 100644 --- a/yudao-module-system/yudao-module-system-biz/pom.xml +++ b/yudao-module-system/yudao-module-system-biz/pom.xml @@ -97,6 +97,11 @@ yudao-spring-boot-starter-excel + + cn.iocoder.boot + yudao-spring-boot-starter-captcha + + diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 0a136551f..9a9c0a95e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -55,7 +55,6 @@ public class AuthController { private PermissionService permissionService; @Resource private SocialUserService socialUserService; - @Resource private SecurityProperties securityProperties; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java index 67e80d24a..bafc322e2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java @@ -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; // ========== 绑定社交登录时,需要传递如下参数 ========== diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.http deleted file mode 100644 index 2033fac31..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.http +++ /dev/null @@ -1,3 +0,0 @@ -### 请求 /captcha/get-image 接口 => 成功 -GET {{baseUrl}}/system/captcha/get-image -tenant-id: {{adminTenentId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.java deleted file mode 100644 index 546bbde00..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/CaptchaController.java +++ /dev/null @@ -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 getCaptchaImage() { - return success(captchaService.getCaptchaImage()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/vo/CaptchaImageRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/vo/CaptchaImageRespVO.java deleted file mode 100644 index 382fafcb5..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/common/vo/CaptchaImageRespVO.java +++ /dev/null @@ -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; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/common/CaptchaConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/common/CaptchaConvert.java deleted file mode 100644 index 54d36bee9..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/common/CaptchaConvert.java +++ /dev/null @@ -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(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaConfig.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaConfig.java deleted file mode 100644 index 4028f6cef..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaConfig.java +++ /dev/null @@ -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 { -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaProperties.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaProperties.java deleted file mode 100644 index 0d7cd0d20..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/CaptchaProperties.java +++ /dev/null @@ -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; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java deleted file mode 100644 index ee406c079..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 基于 Hutool captcha 库,实现验证码功能 - */ -package cn.iocoder.yudao.module.system.framework.captcha; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 8f54e9f61..ce860add5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -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); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaService.java deleted file mode 100644 index ecb05d88a..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaService.java +++ /dev/null @@ -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); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceImpl.java deleted file mode 100644 index f52f0ba3b..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceImpl.java +++ /dev/null @@ -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); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index ac1736959..5f4c9a2f2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -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; } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 435e5791f..218778ac9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -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() { diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceTest.java deleted file mode 100644 index 1948538d3..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/common/CaptchaServiceTest.java +++ /dev/null @@ -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)); - } - -} diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 55c91e2f8..852a9dff0 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -51,6 +51,16 @@ yudao-module-product-biz ${revision} + + cn.iocoder.boot + yudao-module-trade-biz + ${revision} + + + cn.iocoder.boot + yudao-module-coupon-biz + ${revision} + diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index d9513e738..9b4340c73 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -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 请求微信公众号平台 --- #################### 芋道相关配置 #################### diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 073584275..c26354ead 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,33 +45,33 @@ spring: datasource: 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://139.9.196.247: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 连接的示例 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://139.9.196.247: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 连接的示例 username: root password: 123456 -# username: sa -# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W + # username: sa + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: - host: 127.0.0.1 # 地址 + host: 139.9.196.247 # 地址 port: 6379 # 端口 database: 0 # 数据库索引 -# password: 123456 # 密码,建议生产环境开启 + password: 123456 # 密码,建议生产环境开启 jasypt: encryptor: diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 6de6ae46d..85d52550d 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -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 中文转 Unicode,Linux 可能需要转 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 diff --git a/yudao-ui-admin-uniapp/api/login.js b/yudao-ui-admin-uniapp/api/login.js index 628e2a741..4a00d51f7 100644 --- a/yudao-ui-admin-uniapp/api/login.js +++ b/yudao-ui-admin-uniapp/api/login.js @@ -1,47 +1,60 @@ import request from '@/utils/request' // 登录方法 -export function login(username, password, code, uuid) { - const data = { - username, - password, - code, - uuid - } - return request({ - url: '/system/auth/login', - headers: { - isToken: false - }, - 'method': 'post', - 'data': data - }) +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' - }) + return request({ + url: '/system/auth/get-permission-info', + 'method': 'GET' + }) } // 退出方法 export function logout() { - return request({ - url: '/system/auth/logout', - 'method': 'post' - }) + return request({ + url: '/system/auth/logout', + 'method': 'POST' + }) } // 获取验证码 -export function getCodeImg() { - return request({ - url: '/system/captcha/get-image', - headers: { - isToken: false - }, - method: 'get', - timeout: 20000 - }) +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 + }) } diff --git a/yudao-ui-admin-uniapp/api/system/user.js b/yudao-ui-admin-uniapp/api/system/user.js index 9e9e7bf5e..59e9304cc 100644 --- a/yudao-ui-admin-uniapp/api/system/user.js +++ b/yudao-ui-admin-uniapp/api/system/user.js @@ -9,7 +9,7 @@ export function updateUserPwd(oldPassword, newPassword) { } return request({ url: '/system/user/profile/update-password', - method: 'put', + method: 'PUT', params: data }) } @@ -18,7 +18,7 @@ export function updateUserPwd(oldPassword, newPassword) { export function getUserProfile() { return request({ url: '/system/user/profile/get', - method: 'get' + method: 'GET' }) } @@ -26,7 +26,7 @@ export function getUserProfile() { export function updateUserProfile(data) { return request({ url: '/system/user/profile/update', - method: 'put', + method: 'PUT', data: data }) } @@ -35,7 +35,7 @@ export function updateUserProfile(data) { export function uploadAvatar(data) { return upload({ url: '/system/user/profile/update-avatar', - method: 'put', + method: 'PUT', name: data.name, filePath: data.filePath }) diff --git a/yudao-ui-admin-uniapp/components/verifition/Verify.vue b/yudao-ui-admin-uniapp/components/verifition/Verify.vue new file mode 100644 index 000000000..3fe4d884f --- /dev/null +++ b/yudao-ui-admin-uniapp/components/verifition/Verify.vue @@ -0,0 +1,469 @@ + + + diff --git a/yudao-ui-admin-uniapp/components/verifition/utils/ase.js b/yudao-ui-admin-uniapp/components/verifition/utils/ase.js new file mode 100644 index 000000000..1fdceed4f --- /dev/null +++ b/yudao-ui-admin-uniapp/components/verifition/utils/ase.js @@ -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(); +} diff --git a/yudao-ui-admin-uniapp/components/verifition/utils/request.js b/yudao-ui-admin-uniapp/components/verifition/utils/request.js new file mode 100644 index 000000000..e6a31b02a --- /dev/null +++ b/yudao-ui-admin-uniapp/components/verifition/utils/request.js @@ -0,0 +1,17 @@ +import config from '@/config' +const baseUrl = config.baseUrl +export const myRequest = (option = {}) => { + return new Promise((reslove, reject) => { + uni.request({ + url: baseUrl + option.url, + data: option.data, + method: option.method || "GET", + success: (result) => { + reslove(result) + }, + fail: (error) => { + reject(error) + } + }) + }) +} diff --git a/yudao-ui-admin-uniapp/components/verifition/verifyPoint/verifyPoint.vue b/yudao-ui-admin-uniapp/components/verifition/verifyPoint/verifyPoint.vue new file mode 100644 index 000000000..ea794f4c9 --- /dev/null +++ b/yudao-ui-admin-uniapp/components/verifition/verifyPoint/verifyPoint.vue @@ -0,0 +1,555 @@ + + + + diff --git a/yudao-ui-admin-uniapp/components/verifition/verifySlider/verifySlider.vue b/yudao-ui-admin-uniapp/components/verifition/verifySlider/verifySlider.vue new file mode 100644 index 000000000..24ccd1aeb --- /dev/null +++ b/yudao-ui-admin-uniapp/components/verifition/verifySlider/verifySlider.vue @@ -0,0 +1,659 @@ + + + diff --git a/yudao-ui-admin-uniapp/config.js b/yudao-ui-admin-uniapp/config.js index cd0a162b3..d8d38e8d2 100644 --- a/yudao-ui-admin-uniapp/config.js +++ b/yudao-ui-admin-uniapp/config.js @@ -1,7 +1,8 @@ // 应用全局配置 module.exports = { // baseUrl: 'http://localhost:8080', - baseUrl: 'http://localhost:48080/admin-api', + baseUrl: 'http://localhost:48080', + baseApi: '/admin-api', // 应用信息 appInfo: { // 应用名称 diff --git a/yudao-ui-admin-uniapp/package.json b/yudao-ui-admin-uniapp/package.json new file mode 100644 index 000000000..e5def356f --- /dev/null +++ b/yudao-ui-admin-uniapp/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "crypto-js": "^4.0.0" + } +} diff --git a/yudao-ui-admin-uniapp/pages/login.vue b/yudao-ui-admin-uniapp/pages/login.vue index 24bbdfd05..bb7908eb9 100644 --- a/yudao-ui-admin-uniapp/pages/login.vue +++ b/yudao-ui-admin-uniapp/pages/login.vue @@ -1,182 +1,168 @@ diff --git a/yudao-ui-admin-uniapp/static/images/default.jpg b/yudao-ui-admin-uniapp/static/images/default.jpg new file mode 100644 index 000000000..aa0237bb9 Binary files /dev/null and b/yudao-ui-admin-uniapp/static/images/default.jpg differ diff --git a/yudao-ui-admin-uniapp/store/modules/user.js b/yudao-ui-admin-uniapp/store/modules/user.js index d49cb9a9d..7d03c1a23 100644 --- a/yudao-ui-admin-uniapp/store/modules/user.js +++ b/yudao-ui-admin-uniapp/store/modules/user.js @@ -42,10 +42,9 @@ const user = { Login({ commit }, userInfo) { const username = userInfo.username.trim() const password = userInfo.password - const code = userInfo.code - const uuid = userInfo.uuid + const captchaVerification = userInfo.captchaVerification return new Promise((resolve, reject) => { - login(username, password, code, uuid).then(res => { + login(username, password, captchaVerification).then(res => { res = res.data; // 设置 token setToken(res) @@ -83,7 +82,6 @@ const user = { LogOut({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { - commit('SET_TOKEN', '') commit('SET_ROLES', []) commit('SET_PERMISSIONS', []) removeToken() diff --git a/yudao-ui-admin-uniapp/utils/request.js b/yudao-ui-admin-uniapp/utils/request.js index 6c00a3f09..4799e7467 100644 --- a/yudao-ui-admin-uniapp/utils/request.js +++ b/yudao-ui-admin-uniapp/utils/request.js @@ -5,7 +5,7 @@ import errorCode from '@/utils/errorCode' import { toast, showConfirm, tansParams } from '@/utils/common' let timeout = 10000 -const baseUrl = config.baseUrl +const baseUrl = config.baseUrl + config.baseApi; const request = config => { // 是否需要设置 token diff --git a/yudao-ui-admin-vue3/.env b/yudao-ui-admin-vue3/.env index 1bcb92da6..a7bbb6b7f 100644 --- a/yudao-ui-admin-vue3/.env +++ b/yudao-ui-admin-vue3/.env @@ -9,3 +9,6 @@ VITE_OPEN=true # 租户开关 VITE_APP_TENANT_ENABLE=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=false diff --git a/yudao-ui-admin-vue3/README.md b/yudao-ui-admin-vue3/README.md index c477d2e75..8379c7f7e 100644 --- a/yudao-ui-admin-vue3/README.md +++ b/yudao-ui-admin-vue3/README.md @@ -10,7 +10,6 @@ Prettier Less Taiwind -

## 介绍 diff --git a/yudao-ui-admin-vue3/package.json b/yudao-ui-admin-vue3/package.json index c221f0d68..81c41cc42 100644 --- a/yudao-ui-admin-vue3/package.json +++ b/yudao-ui-admin-vue3/package.json @@ -26,12 +26,13 @@ }, "dependencies": { "@iconify/iconify": "^2.2.1", - "@vueuse/core": "^9.0.2", + "@vueuse/core": "^9.1.0", "@wangeditor/editor": "^5.1.14", "@wangeditor/editor-for-vue": "^5.1.10", - "@zxcvbn-ts/core": "^2.0.3", + "@zxcvbn-ts/core": "^2.0.4", "animate.css": "^4.1.1", "axios": "^0.27.2", + "crypto-js": "^4.1.1", "dayjs": "^1.11.4", "echarts": "^5.3.3", "echarts-wordcloud": "^2.0.0", @@ -48,7 +49,7 @@ "url": "^0.11.0", "vue": "3.2.37", "vue-cropper": "^1.0.3", - "vue-i18n": "9.2.0", + "vue-i18n": "9.2.2", "vue-router": "^4.1.3", "vue-types": "^4.2.1", "web-storage-cache": "^1.1.1" @@ -56,17 +57,17 @@ "devDependencies": { "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", - "@iconify/json": "^2.1.86", - "@intlify/vite-plugin-vue-i18n": "^5.0.1", - "@purge-icons/generated": "^0.8.1", + "@iconify/json": "^2.1.89", + "@intlify/vite-plugin-vue-i18n": "^6.0.0", + "@purge-icons/generated": "^0.9.0", "@types/intro.js": "^5.1.0", "@types/lodash-es": "^4.17.6", - "@types/node": "^18.6.3", + "@types/node": "^18.6.5", "@types/nprogress": "^0.2.0", "@types/qrcode": "^1.4.2", "@types/qs": "^6.9.7", - "@typescript-eslint/eslint-plugin": "^5.32.0", - "@typescript-eslint/parser": "^5.32.0", + "@typescript-eslint/eslint-plugin": "^5.33.0", + "@typescript-eslint/parser": "^5.33.0", "@vitejs/plugin-vue": "^3.0.1", "@vitejs/plugin-vue-jsx": "^2.0.0", "autoprefixer": "^10.4.8", @@ -78,7 +79,7 @@ "less": "^4.1.3", "lint-staged": "^13.0.3", "plop": "^3.1.1", - "postcss": "^8.4.14", + "postcss": "^8.4.16", "postcss-html": "^1.5.0", "postcss-less": "^6.0.0", "prettier": "^2.7.1", @@ -91,16 +92,16 @@ "stylelint-config-standard": "^26.0.0", "stylelint-order": "^5.0.0", "typescript": "4.7.4", - "unplugin-vue-define-options": "^0.7.1", - "vite": "3.0.4", + "unplugin-vue-define-options": "^0.7.3", + "vite": "3.0.5", "vite-plugin-compression": "^0.5.1", "vite-plugin-eslint": "^1.7.0", "vite-plugin-html": "^3.2.0", - "vite-plugin-purge-icons": "^0.8.2", + "vite-plugin-purge-icons": "^0.9.0", "vite-plugin-style-import": "^2.0.0", "vite-plugin-svg-icons": "^2.0.1", "vite-plugin-windicss": "^1.8.7", - "vue-tsc": "^0.39.4", + "vue-tsc": "^0.39.5", "windicss": "^3.5.6" }, "engines": { diff --git a/yudao-ui-admin-vue3/src/api/login/index.ts b/yudao-ui-admin-vue3/src/api/login/index.ts index 485d273c9..6b53f84e5 100644 --- a/yudao-ui-admin-vue3/src/api/login/index.ts +++ b/yudao-ui-admin-vue3/src/api/login/index.ts @@ -18,11 +18,6 @@ export interface SmsLoginVO { code: string } -// 获取验证码 -export const getCodeImgApi = () => { - return request.get({ url: '/system/captcha/get-image' }) -} - // 登录 export const loginApi = (data: UserLoginVO) => { return request.post({ url: '/system/auth/login', data }) diff --git a/yudao-ui-admin-vue3/src/api/login/types.ts b/yudao-ui-admin-vue3/src/api/login/types.ts index 75abe28ce..1a91aecc1 100644 --- a/yudao-ui-admin-vue3/src/api/login/types.ts +++ b/yudao-ui-admin-vue3/src/api/login/types.ts @@ -1,8 +1,7 @@ export type UserLoginVO = { username: string password: string - code: string - uuid: string + captchaVerification: string } export type TokenType = { diff --git a/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts b/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts index a5a24490f..3eab59e01 100644 --- a/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts +++ b/yudao-ui-admin-vue3/src/api/system/user/profile/index.ts @@ -24,6 +24,6 @@ export const updateUserPwdApi = (oldPassword: string, newPassword: string) => { } // 用户头像上传 -export const uploadAvatarApi = (params) => { - return request.upload({ url: '/system/user/profile/update-avatar', params }) +export const uploadAvatarApi = (data) => { + return request.upload({ url: '/system/user/profile/update-avatar', data: data }) } diff --git a/yudao-ui-admin-vue3/src/components/DictTag/src/DictTag.vue b/yudao-ui-admin-vue3/src/components/DictTag/src/DictTag.vue index 0ad3ec69a..5bbb9104c 100644 --- a/yudao-ui-admin-vue3/src/components/DictTag/src/DictTag.vue +++ b/yudao-ui-admin-vue3/src/components/DictTag/src/DictTag.vue @@ -8,7 +8,7 @@ const props = defineProps({ required: true }, value: { - type: [String, Number] as PropType, + type: [String, Number, Boolean] as PropType, required: true } }) diff --git a/yudao-ui-admin-vue3/src/components/Editor/src/Editor.vue b/yudao-ui-admin-vue3/src/components/Editor/src/Editor.vue index f623b735a..f78ffd5a9 100644 --- a/yudao-ui-admin-vue3/src/components/Editor/src/Editor.vue +++ b/yudao-ui-admin-vue3/src/components/Editor/src/Editor.vue @@ -8,6 +8,8 @@ import { ElMessage } from 'element-plus' import { useLocaleStore } from '@/store/modules/locale' import { getAccessToken, getTenantId } from '@/utils/auth' +type InsertFnType = (url: string, alt: string, href: string) => void + const localeStore = useLocaleStore() const currentLocale = computed(() => localeStore.getCurrentLocale) @@ -85,29 +87,58 @@ const editorConfig = computed((): IEditorConfig => { ['uploadImage']: { server: import.meta.env.VITE_UPLOAD_URL, // 单个文件的最大体积限制,默认为 2M - maxFileSize: 2 * 1024 * 1024, + maxFileSize: 5 * 1024 * 1024, // 最多可上传几个文件,默认为 100 maxNumberOfFiles: 10, // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] allowedFileTypes: ['image/*'], // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。 - meta: {}, + meta: { updateSupport: 0 }, // 将 meta 拼接到 url 参数中,默认 false - metaWithUrl: false, + metaWithUrl: true, // 自定义增加 http header headers: { - Accept: 'image/*', + Accept: '*', Authorization: 'Bearer ' + getAccessToken(), 'tenant-id': getTenantId() }, // 跨域是否传递 cookie ,默认为 false - withCredentials: false, + withCredentials: true, // 超时时间,默认为 10 秒 - timeout: 5 * 1000 // 5 秒 + timeout: 5 * 1000, // 5 秒 + + // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image + fieldName: 'file', + + // 上传之前触发 + onBeforeUpload(file: File) { + console.log(file) + return file + }, + // 上传进度的回调函数 + onProgress(progress: number) { + // progress 是 0-100 的数字 + console.log('progress', progress) + }, + onSuccess(file: File, res: any) { + console.log('onSuccess', file, res) + }, + onFailed(file: File, res: any) { + alert(res.message) + console.log('onFailed', file, res) + }, + onError(file: File, err: any, res: any) { + alert(err.message) + console.error('onError', file, err, res) + }, + // 自定义插入图片 + customInsert(res: any, insertFn: InsertFnType) { + insertFn(res.data, 'image', res.data) + } } }, uploadImgShowBase64: true diff --git a/yudao-ui-admin-vue3/src/components/UserInfo/src/UserInfo.vue b/yudao-ui-admin-vue3/src/components/UserInfo/src/UserInfo.vue index 47c48213d..5bccbe429 100644 --- a/yudao-ui-admin-vue3/src/components/UserInfo/src/UserInfo.vue +++ b/yudao-ui-admin-vue3/src/components/UserInfo/src/UserInfo.vue @@ -2,18 +2,11 @@ import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus' import { useI18n } from '@/hooks/web/useI18n' import { useCache } from '@/hooks/web/useCache' -import { removeToken } from '@/utils/auth' -import { resetRouter } from '@/router' import { useRouter } from 'vue-router' import { useDesign } from '@/hooks/web/useDesign' -import { useTagsViewStore } from '@/store/modules/tagsView' import avatarImg from '@/assets/imgs/avatar.gif' - -const tagsViewStore = useTagsViewStore() - -const { getPrefixCls } = useDesign() - -const prefixCls = getPrefixCls('user-info') +import { useUserStore } from '@/store/modules/user' +import { useTagsViewStore } from '@/store/modules/tagsView' const { t } = useI18n() @@ -21,6 +14,14 @@ const { wsCache } = useCache() const { push, replace } = useRouter() +const userStore = useUserStore() + +const tagsViewStore = useTagsViewStore() + +const { getPrefixCls } = useDesign() + +const prefixCls = getPrefixCls('user-info') + const user = wsCache.get('user') const avatar = user.user.avatar ? user.user.avatar : avatarImg @@ -34,10 +35,8 @@ const loginOut = () => { type: 'warning' }) .then(async () => { - resetRouter() // 重置静态路由表 - wsCache.clear() - removeToken() - tagsViewStore.delAllViews() + userStore.loginOut() + tagsViewStore.delAllViews replace('/login') }) .catch(() => {}) diff --git a/yudao-ui-admin-vue3/src/components/Verifition/index.ts b/yudao-ui-admin-vue3/src/components/Verifition/index.ts new file mode 100644 index 000000000..bcfe6d940 --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/index.ts @@ -0,0 +1,3 @@ +import Verify from './src/Verify.vue' + +export { Verify } diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue new file mode 100644 index 000000000..70bfce7ef --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify.vue @@ -0,0 +1,438 @@ + + + diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue new file mode 100644 index 000000000..ea02b7834 --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue @@ -0,0 +1,281 @@ + + diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue new file mode 100644 index 000000000..2d59023bd --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue @@ -0,0 +1,426 @@ + + diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts new file mode 100644 index 000000000..0daa63a56 --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts @@ -0,0 +1,4 @@ +import VerifySlide from './VerifySlide.vue' +import VerifyPoints from './VerifyPoints.vue' + +export { VerifySlide, VerifyPoints } diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/api/index.ts b/yudao-ui-admin-vue3/src/components/Verifition/src/api/index.ts new file mode 100644 index 000000000..6a67b7119 --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/api/index.ts @@ -0,0 +1,24 @@ +/** + * 此处可直接引用自己项目封装好的 axios 配合后端联调 + */ + +import request from './../utils/axios' //组件内部封装的axios +// import request from "@/api/axios.js" //调用项目封装的axios + +//获取验证图片 以及token +export function reqGet(data) { + return request({ + url: '/captcha/get', + method: 'post', + data + }) +} + +//滑动或者点选验证 +export function reqCheck(data) { + return request({ + url: '/captcha/check', + method: 'post', + data + }) +} diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts new file mode 100644 index 000000000..d2e6b988f --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts @@ -0,0 +1,14 @@ +import CryptoJS from 'crypto-js' +/** + * @word 要加密的内容 + * @keyWord String 服务器随机返回的关键字 + * */ +export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') { + const key = CryptoJS.enc.Utf8.parse(keyWord) + const srcs = CryptoJS.enc.Utf8.parse(word) + const encrypted = CryptoJS.AES.encrypt(srcs, key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }) + return encrypted.toString() +} diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/utils/axios.ts b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/axios.ts new file mode 100644 index 000000000..ca68097e7 --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/axios.ts @@ -0,0 +1,26 @@ +import axios from 'axios' + +axios.defaults.baseURL = import.meta.env.VITE_BASE_URL + +const service = axios.create({ + timeout: 40000, + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/json; charset=UTF-8' + } +}) +service.interceptors.request.use( + (config) => { + return config + }, + (error) => { + Promise.reject(error) + } +) + +// response interceptor +service.interceptors.response.use((response) => { + const res = response.data + return res +}) +export default service diff --git a/yudao-ui-admin-vue3/src/components/Verifition/src/utils/util.ts b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/util.ts new file mode 100644 index 000000000..15c16270d --- /dev/null +++ b/yudao-ui-admin-vue3/src/components/Verifition/src/utils/util.ts @@ -0,0 +1,97 @@ +export function resetSize(vm) { + let img_width, img_height, bar_width, bar_height //图片的宽度、高度,移动条的宽度、高度 + const EmployeeWindow = window as any + const parentWidth = vm.$el.parentNode.offsetWidth || EmployeeWindow.offsetWidth + const parentHeight = vm.$el.parentNode.offsetHeight || EmployeeWindow.offsetHeight + if (vm.imgSize.width.indexOf('%') != -1) { + img_width = (parseInt(vm.imgSize.width) / 100) * parentWidth + 'px' + } else { + img_width = vm.imgSize.width + } + + if (vm.imgSize.height.indexOf('%') != -1) { + img_height = (parseInt(vm.imgSize.height) / 100) * parentHeight + 'px' + } else { + img_height = vm.imgSize.height + } + + if (vm.barSize.width.indexOf('%') != -1) { + bar_width = (parseInt(vm.barSize.width) / 100) * parentWidth + 'px' + } else { + bar_width = vm.barSize.width + } + + if (vm.barSize.height.indexOf('%') != -1) { + bar_height = (parseInt(vm.barSize.height) / 100) * parentHeight + 'px' + } else { + bar_height = vm.barSize.height + } + + return { imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height } +} + +export const _code_chars = [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z' +] +export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0'] +export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC'] diff --git a/yudao-ui-admin-vue3/src/config/axios/config.ts b/yudao-ui-admin-vue3/src/config/axios/config.ts index 050ce251b..811650873 100644 --- a/yudao-ui-admin-vue3/src/config/axios/config.ts +++ b/yudao-ui-admin-vue3/src/config/axios/config.ts @@ -1,10 +1,5 @@ const config: { - base_url: { - base: string - dev: string - pro: string - test: string - } + base_url: string result_code: number | string default_headers: AxiosHeaders request_timeout: number @@ -12,20 +7,7 @@ const config: { /** * api请求基础路径 */ - base_url: { - // 开发环境接口前缀 - base: '', - - // 打包开发环境接口前缀 - dev: '', - - // 打包生产环境接口前缀 - pro: '', - - // 打包测试环境接口前缀 - test: '' - }, - + base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL, /** * 接口成功返回状态码 */ diff --git a/yudao-ui-admin-vue3/src/config/axios/index.ts b/yudao-ui-admin-vue3/src/config/axios/index.ts index 14bd90bad..75d4139d1 100644 --- a/yudao-ui-admin-vue3/src/config/axios/index.ts +++ b/yudao-ui-admin-vue3/src/config/axios/index.ts @@ -1,15 +1,15 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' -import { ElMessage, ElNotification } from 'element-plus' +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' import qs from 'qs' import { config } from '@/config/axios/config' -import { getAccessToken, getTenantId, removeToken } from '@/utils/auth' +import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth' import errorCode from './errorCode' import { useI18n } from '@/hooks/web/useI18n' +import { resetRouter } from '@/router' +import { useCache } from '@/hooks/web/useCache' const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE -const BASE_URL = import.meta.env.VITE_BASE_URL -const BASE_API = import.meta.env.VITE_API_URL -const { result_code, base_url } = config +const { result_code, base_url, request_timeout } = config // 需要忽略的提示。忽略后,自动 Promise.reject('error') const ignoreMsgs = [ @@ -20,16 +20,14 @@ const ignoreMsgs = [ export const isRelogin = { show: false } // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 // 请求队列 -// const requestList = [] +let requestList: any[] = [] // 是否正在刷新中 -// const isRefreshToken = false - -export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH] +let isRefreshToken = false // 创建axios实例 const service: AxiosInstance = axios.create({ - baseURL: BASE_URL + BASE_API, // api 的 base_url - timeout: config.request_timeout, // 请求超时时间 + baseURL: base_url, // api 的 base_url + timeout: request_timeout, // 请求超时时间 withCredentials: false // 禁用 Cookie 等信息 }) @@ -112,16 +110,38 @@ service.interceptors.response.use( return Promise.reject(msg) } else if (code === 401) { // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 - return handleAuthorized() - // if (!isRefreshToken) { - // isRefreshToken = true - // // 1. 如果获取不到刷新令牌,则只能执行登出操作 - // if (!getRefreshToken()) { - // return handleAuthorized() - // } - // // 2. 进行刷新访问令牌 - // // TODO: 引入refreshToken会循环依赖报错 - // } + if (!isRefreshToken) { + isRefreshToken = true + // 1. 如果获取不到刷新令牌,则只能执行登出操作 + if (!getRefreshToken()) { + return handleAuthorized() + } + // 2. 进行刷新访问令牌 + try { + const refreshTokenRes = await refreshToken() + // 2.1 刷新成功,则回放队列的请求 + 当前请求 + setToken(refreshTokenRes.data) + requestList.forEach((cb: any) => cb()) + return service(response.config) + } catch (e) { + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 + // 2.2 刷新失败,只回放队列的请求 + requestList.forEach((cb: any) => cb()) + // 提示是否要登出。即不回放当前请求!不然会形成递归 + return handleAuthorized() + } finally { + requestList = [] + isRefreshToken = false + } + } else { + // 添加到队列,等待刷新获取到新的令牌 + return new Promise((resolve) => { + requestList.push(() => { + ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + resolve(service(response.config)) + }) + }) + } } else if (code === 500) { ElMessage.error(t('sys.api.errMsg500')) return Promise.reject(new Error(msg)) @@ -165,12 +185,33 @@ service.interceptors.response.use( return Promise.reject(error) } ) + +const refreshToken = async () => { + return await service({ + url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(), + method: 'post' + }) +} const handleAuthorized = () => { const { t } = useI18n() if (!isRelogin.show) { - removeToken() isRelogin.show = true - ElNotification.error(t('sys.api.timeoutMessage')) + ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { + confirmButtonText: t('login.relogin'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + .then(() => { + const { wsCache } = useCache() + resetRouter() // 重置静态路由表 + wsCache.clear() + removeToken() + isRelogin.show = false + window.location.href = '/' + }) + .catch(() => { + isRelogin.show = false + }) } return Promise.reject(t('sys.api.timeoutMessage')) } diff --git a/yudao-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts b/yudao-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts index e26d2dbac..b75c7db88 100644 --- a/yudao-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts +++ b/yudao-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts @@ -80,6 +80,8 @@ const filterSearchSchema = (crudSchema: CrudSchema[]): FormSchema[] => { const options: ComponentOptions[] = [] let comonentProps = {} if (schemaItem.dictType) { + const allOptions: ComponentOptions = { label: '全部', value: '' } + options.push(allOptions) getIntDictOptions(schemaItem.dictType).forEach((dict) => { options.push(dict) }) diff --git a/yudao-ui-admin-vue3/src/locales/en.ts b/yudao-ui-admin-vue3/src/locales/en.ts index b31226dad..a7463a4f3 100644 --- a/yudao-ui-admin-vue3/src/locales/en.ts +++ b/yudao-ui-admin-vue3/src/locales/en.ts @@ -128,6 +128,13 @@ export default { btnRegister: 'Sign up', SmsSendMsg: 'code has been sent' }, + captcha: { + verification: 'Please complete security verification', + slide: 'Swipe right to complete verification', + point: 'Please click', + success: 'Verification succeeded', + fail: 'verification failed' + }, router: { login: 'Login', home: 'Home', @@ -191,7 +198,6 @@ export default { yield: 'Yield', dynamic: 'Dynamic', push: 'push', - pushCode: 'push code to Github', follow: 'Follow' }, form: { diff --git a/yudao-ui-admin-vue3/src/locales/zh-CN.ts b/yudao-ui-admin-vue3/src/locales/zh-CN.ts index 9659babc3..57a684c00 100644 --- a/yudao-ui-admin-vue3/src/locales/zh-CN.ts +++ b/yudao-ui-admin-vue3/src/locales/zh-CN.ts @@ -128,6 +128,13 @@ export default { btnRegister: '注册', SmsSendMsg: '验证码已发送' }, + captcha: { + verification: '请完成安全验证', + slide: '向右滑动完成验证', + point: '请依次点击', + success: '验证成功', + fail: '验证失败' + }, router: { login: '登录', home: '首页', @@ -191,7 +198,6 @@ export default { yield: '产量', dynamic: '动态', push: '推送', - pushCode: '推送 代码到 Github', follow: '关注' }, form: { diff --git a/yudao-ui-admin-vue3/src/router/index.ts b/yudao-ui-admin-vue3/src/router/index.ts index 812ec4f7a..6cdec5ee1 100644 --- a/yudao-ui-admin-vue3/src/router/index.ts +++ b/yudao-ui-admin-vue3/src/router/index.ts @@ -2,20 +2,16 @@ import type { App } from 'vue' import { getAccessToken } from '@/utils/auth' import type { RouteRecordRaw } from 'vue-router' import remainingRouter from './modules/remaining' -import { useCache } from '@/hooks/web/useCache' import { useTitle } from '@/hooks/web/useTitle' import { useNProgress } from '@/hooks/web/useNProgress' import { usePageLoading } from '@/hooks/web/usePageLoading' import { createRouter, createWebHashHistory } from 'vue-router' import { usePermissionStoreWithOut } from '@/store/modules/permission' import { useDictStoreWithOut } from '@/store/modules/dict' +import { useUserStoreWithOut } from '@/store/modules/user' import { listSimpleDictDataApi } from '@/api/system/dict/dict.data' - -const permissionStore = usePermissionStoreWithOut() - -const dictStore = useDictStoreWithOut() - -const { wsCache } = useCache() +import { isRelogin } from '@/config/axios' +import { getInfoApi } from '@/api/login' const { start, done } = useNProgress() @@ -23,7 +19,7 @@ const { loadStart, loadDone } = usePageLoading() // 创建路由实例 const router = createRouter({ - history: createWebHashHistory(), + history: createWebHashHistory(), // createWebHashHistory URL带#,createWebHistory URL不带# strict: true, routes: remainingRouter as RouteRecordRaw[], scrollBehavior: () => ({ left: 0, top: 0 }) @@ -47,37 +43,37 @@ router.beforeEach(async (to, from, next) => { if (to.path === '/login') { next({ path: '/' }) } else { - if (!dictStore.getIsSetDict) { - // 获取所有字典 + // 获取所有字典 + const dictStore = useDictStoreWithOut() + const userStore = useUserStoreWithOut() + const permissionStore = usePermissionStoreWithOut() + if (!dictStore.getHasDictData) { const res = await listSimpleDictDataApi() - if (res) { - dictStore.setDictMap(res) - dictStore.setIsSetDict(true) - } + dictStore.setDictMap(res) } - if (permissionStore.getIsAddRouters) { + if (userStore.getRoles.length === 0) { + isRelogin.show = true + const res = await getInfoApi() + await userStore.setUserInfoAction(res) + isRelogin.show = false + // 后端过滤菜单 + await permissionStore.generateRoutes() + permissionStore.getAddRouters.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表 + }) + const redirectPath = from.query.redirect || to.path + const redirect = decodeURIComponent(redirectPath as string) + const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } + next(nextData) + } else { next() - return } - // 开发者可根据实际情况进行修改 - const roleRouters = wsCache.get('roleRouters') || [] - - await permissionStore.generateRoutes(roleRouters as AppCustomRouteRecordRaw[]) - - permissionStore.getAddRouters.forEach((route) => { - router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表 - }) - const redirectPath = from.query.redirect || to.path - const redirect = decodeURIComponent(redirectPath as string) - const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } - permissionStore.setIsAddRouters(true) - next(nextData) } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { - next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 } } }) diff --git a/yudao-ui-admin-vue3/src/store/modules/dict.ts b/yudao-ui-admin-vue3/src/store/modules/dict.ts index 11ee352c1..7cd1d725b 100644 --- a/yudao-ui-admin-vue3/src/store/modules/dict.ts +++ b/yudao-ui-admin-vue3/src/store/modules/dict.ts @@ -13,14 +13,12 @@ export interface DictTypeType { dictValue: DictValueType[] } export interface DictState { - isSetDict: boolean dictMap: Recordable } export const useDictStore = defineStore({ id: 'dict', state: (): DictState => ({ - isSetDict: false, dictMap: {} }), persist: { @@ -30,8 +28,12 @@ export const useDictStore = defineStore({ getDictMap(): Recordable { return this.dictMap }, - getIsSetDict(): boolean { - return this.isSetDict + getHasDictData(): boolean { + if (this.dictMap.length > 0) { + return true + } else { + return false + } } }, actions: { @@ -53,9 +55,6 @@ export const useDictStore = defineStore({ }) }) this.dictMap = dictMap - }, - setIsSetDict(isSetDict: boolean) { - this.isSetDict = isSetDict } } }) diff --git a/yudao-ui-admin-vue3/src/store/modules/permission.ts b/yudao-ui-admin-vue3/src/store/modules/permission.ts index dfce2949c..cb76ef6f5 100644 --- a/yudao-ui-admin-vue3/src/store/modules/permission.ts +++ b/yudao-ui-admin-vue3/src/store/modules/permission.ts @@ -2,12 +2,15 @@ import { defineStore } from 'pinia' import { store } from '../index' import { cloneDeep } from 'lodash-es' import remainingRouter from '@/router/modules/remaining' -import { generateRoutes, flatMultiLevelRoutes } from '@/utils/routerHelper' +import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper' +import { getAsyncRoutesApi } from '@/api/login' +import { useCache } from '@/hooks/web/useCache' + +const { wsCache } = useCache() export interface PermissionState { routers: AppRouteRecordRaw[] addRouters: AppRouteRecordRaw[] - isAddRouters: boolean menuTabRouters: AppRouteRecordRaw[] } @@ -16,7 +19,6 @@ export const usePermissionStore = defineStore({ state: (): PermissionState => ({ routers: [], addRouters: [], - isAddRouters: false, menuTabRouters: [] }), persist: { @@ -29,18 +31,21 @@ export const usePermissionStore = defineStore({ getAddRouters(): AppRouteRecordRaw[] { return flatMultiLevelRoutes(cloneDeep(this.addRouters)) }, - getIsAddRouters(): boolean { - return this.isAddRouters - }, getMenuTabRouters(): AppRouteRecordRaw[] { return this.menuTabRouters } }, actions: { - generateRoutes(routers?: AppCustomRouteRecordRaw[] | string[]): Promise { - return new Promise((resolve) => { - let routerMap: AppRouteRecordRaw[] = [] - routerMap = generateRoutes(routers as AppCustomRouteRecordRaw[]) + async generateRoutes(): Promise { + return new Promise(async (resolve) => { + let res: AppCustomRouteRecordRaw[] + if (wsCache.get('roleRouters')) { + res = wsCache.get('roleRouters') as AppCustomRouteRecordRaw[] + } else { + res = await getAsyncRoutesApi() + wsCache.set('roleRouters', res) + } + const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[]) // 动态路由,404一定要放到最后面 this.addRouters = routerMap.concat([ { @@ -58,9 +63,6 @@ export const usePermissionStore = defineStore({ resolve() }) }, - setIsAddRouters(state: boolean): void { - this.isAddRouters = state - }, setMenuTabRouters(routers: AppRouteRecordRaw[]): void { this.menuTabRouters = routers } diff --git a/yudao-ui-admin-vue3/src/store/modules/user.ts b/yudao-ui-admin-vue3/src/store/modules/user.ts index 72889c22c..a8aa17de8 100644 --- a/yudao-ui-admin-vue3/src/store/modules/user.ts +++ b/yudao-ui-admin-vue3/src/store/modules/user.ts @@ -1,18 +1,19 @@ import { store } from '../index' import { defineStore } from 'pinia' -import { getAccessToken } from '@/utils/auth' +import { getAccessToken, removeToken } from '@/utils/auth' import { useCache } from '@/hooks/web/useCache' const { wsCache } = useCache() +interface UserVO { + id: number + avatar: string + nickname: string +} interface UserInfoVO { - permissions: [] - roles: [] - user: { - avatar: string - id: number - nickname: string - } + permissions: string[] + roles: string[] + user: UserVO } export const useUserStore = defineStore({ @@ -26,8 +27,19 @@ export const useUserStore = defineStore({ nickname: '' } }), + getters: { + getPermissions(): string[] { + return this.permissions + }, + getRoles(): string[] { + return this.roles + }, + getUser(): UserVO { + return this.user + } + }, actions: { - async getUserInfoAction(userInfo: UserInfoVO) { + async setUserInfoAction(userInfo: UserInfoVO) { if (!getAccessToken()) { this.resetState() return null @@ -37,6 +49,11 @@ export const useUserStore = defineStore({ this.user = userInfo.user wsCache.set('user', userInfo) }, + loginOut() { + removeToken() + wsCache.clear() + this.resetState() + }, resetState() { this.permissions = [] this.roles = [] diff --git a/yudao-ui-admin-vue3/src/utils/routerHelper.ts b/yudao-ui-admin-vue3/src/utils/routerHelper.ts index a0176ad0f..73e673037 100644 --- a/yudao-ui-admin-vue3/src/utils/routerHelper.ts +++ b/yudao-ui-admin-vue3/src/utils/routerHelper.ts @@ -48,7 +48,7 @@ export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormal } // 后端控制路由生成 -export const generateRoutes = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => { +export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => { const res: AppRouteRecordRaw[] = [] const modulesRoutesKeys = Object.keys(modules) for (const route of routes) { @@ -88,7 +88,7 @@ export const generateRoutes = (routes: AppCustomRouteRecordRaw[]): AppRouteRecor data.component = modules[modulesRoutesKeys[index]] } if (route.children) { - data.children = generateRoutes(route.children) + data.children = generateRoute(route.children) } res.push(data) } diff --git a/yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue b/yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue index bb876e0a3..bb4e73cf5 100644 --- a/yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue +++ b/yudao-ui-admin-vue3/src/views/Login/components/LoginForm.vue @@ -21,22 +21,19 @@ import { getPassword, getTenantName } from '@/utils/auth' -import { useUserStoreWithOut } from '@/store/modules/user' -import { useCache } from '@/hooks/web/useCache' import { usePermissionStore } from '@/store/modules/permission' import { useRouter } from 'vue-router' import { useI18n } from '@/hooks/web/useI18n' import { required } from '@/utils/formRules' import { Icon } from '@/components/Icon' import { LoginStateEnum, useLoginState, useFormValid } from './useLogin' -import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router' +import type { RouteLocationNormalizedLoaded } from 'vue-router' +import { Verify } from '@/components/Verifition' -const { currentRoute, addRoute, push } = useRouter() +const { currentRoute, push } = useRouter() const permissionStore = usePermissionStore() -const userStore = useUserStoreWithOut() const formLogin = ref() const { validForm } = useFormValid(formLogin) -const { wsCache } = useCache() const { setLoginState, getLoginState } = useLoginState() const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN) const iconSize = 30 @@ -46,13 +43,6 @@ const { t } = useI18n() const iconHouse = useIcon({ icon: 'ep:house' }) const iconAvatar = useIcon({ icon: 'ep:avatar' }) const iconLock = useIcon({ icon: 'ep:lock' }) -const iconCircleCheck = useIcon({ icon: 'ep:circle-check' }) -const LoginCaptchaRules = { - tenantName: [required], - username: [required], - password: [required], - code: [required] -} const LoginRules = { tenantName: [required], username: [required], @@ -60,10 +50,9 @@ const LoginRules = { } const loginLoading = ref(false) const loginData = reactive({ - codeImg: '', isShowPassword: false, - captchaEnable: true, - tenantEnable: true, + captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE, + tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE, token: '', loading: { signIn: false @@ -72,20 +61,25 @@ const loginData = reactive({ tenantName: '芋道源码', username: 'admin', password: 'admin123', - rememberMe: false, - code: '', - uuid: '' + captchaVerification: '', + rememberMe: false } }) +// blockPuzzle 滑块 clickWord 点击文字 +const verify = ref() +const captchaType = ref('blockPuzzle') // 获取验证码 const getCode = async () => { - const res = await LoginApi.getCodeImgApi() - loginData.captchaEnable = res.enable - if (res.enable) { - loginData.codeImg = 'data:image/gif;base64,' + res.img - loginData.loginForm.uuid = res.uuid + // 情况一,未开启:则直接登录 + if (!loginData.captchaEnable) { + await handleLogin({}) + return } + + // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录 + // 弹出验证码 + verify.value.show() } //获取租户ID const getTenantId = async () => { @@ -107,37 +101,22 @@ const getCookie = () => { } } // 登录 -const handleLogin = async () => { +const handleLogin = async (params) => { + loginLoading.value = true await getTenantId() const data = await validForm() - if (!data) return - loginLoading.value = true - await LoginApi.loginApi(loginData.loginForm) - .then(async (res) => { - setToken(res) - const userInfo = await LoginApi.getInfoApi() - await userStore.getUserInfoAction(userInfo) - await getRoutes() - }) - .catch(() => { - getCode() - }) - .finally(() => { - loginLoading.value = false - }) -} - -// 获取路由 -const getRoutes = async () => { - // 后端过滤菜单 - const res = await LoginApi.getAsyncRoutesApi() - wsCache.set('roleRouters', res) - await permissionStore.generateRoutes(res) - permissionStore.getAddRouters.forEach((route) => { - addRoute(route as RouteRecordRaw) // 动态添加可访问路由表 - }) - permissionStore.setIsAddRouters(true) + if (!data) { + loginLoading.value = false + return + } + loginData.loginForm.captchaVerification = params.captchaVerification + const res = await LoginApi.loginApi(loginData.loginForm) + setToken(res) + if (!redirect.value) { + redirect.value = '/' + } push({ path: redirect.value || permissionStore.addRouters[0].path }) + loginLoading.value = false } // 社交登录 @@ -159,15 +138,14 @@ watch( immediate: true } ) -onMounted(async () => { - await getCode() +onMounted(() => { getCookie() })