diff --git a/pom.xml b/pom.xml index b3ea38a1a..e3c5072ab 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ yudao-module-system yudao-module-infra yudao-module-pay + yudao-module-mall ${project.artifactId} diff --git a/sql/mall.sql b/sql/mall.sql new file mode 100644 index 000000000..9493c3bcf --- /dev/null +++ b/sql/mall.sql @@ -0,0 +1,287 @@ +/* + Navicat Premium Data Transfer + + Source Server : 127.0.0.1 + Source Server Type : MySQL + Source Server Version : 80026 + Source Host : localhost:3306 + Source Schema : ruoyi-vue-pro + + Target Server Type : MySQL + Target Server Version : 80026 + File Encoding : 65001 + + Date: 05/02/2022 00:50:30 +*/ +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); + + +-- ---------------------------- +-- 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='促销活动'; + + +-- 规格菜单 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); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 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); + + +-- 商品菜单 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); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 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); + + +-- 规格名称表 +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; + +-- 规格值表 +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; + +-- 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; + + +-- 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; + + +---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 (2002, '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/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java index 68e0ead10..cd1b9dc5e 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.framework.common.enums; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Arrays; + /** * 通用状态枚举 * @@ -10,11 +13,14 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum CommonStatusEnum { +public enum CommonStatusEnum implements IntArrayValuable { ENABLE(0, "开启"), DISABLE(1, "关闭"); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray(); + + /** * 状态值 */ @@ -24,4 +30,9 @@ public enum CommonStatusEnum { */ private final String name; + @Override + public int[] array() { + return ARRAYS; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/config/YudaoSocialAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/config/YudaoSocialAutoConfiguration.java index fcae86c5e..078159724 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/config/YudaoSocialAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/config/YudaoSocialAutoConfiguration.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.social.config; -import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; import com.xkcoding.http.HttpUtil; import com.xkcoding.http.support.hutool.HutoolImpl; @@ -11,6 +10,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; /** * 社交自动装配类 @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Configuration; public class YudaoSocialAutoConfiguration { @Bean + @Primary @ConditionalOnProperty(prefix = "justauth", value = "enabled", havingValue = "true", matchIfMissing = true) public YudaoAuthRequestFactory yudaoAuthRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) { // 需要修改 HttpUtil 使用的实现,避免类报错 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java index 8f3cf5d51..b2cd28ec6 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/YudaoAuthRequestFactory.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.social.core; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.framework.social.core.enums.AuthExtendSource; -import cn.iocoder.yudao.framework.social.core.request.AuthWeChatMiniProgramRequest; +import cn.iocoder.yudao.framework.social.core.request.AuthWeChatMiniAppRequest; import com.xkcoding.justauth.AuthRequestFactory; import com.xkcoding.justauth.autoconfigure.JustAuthProperties; import me.zhyd.oauth.cache.AuthStateCache; @@ -20,7 +20,6 @@ import java.lang.reflect.Method; * @author timfruit * @date 2021-10-31 */ -// TODO @timfruit:单测 public class YudaoAuthRequestFactory extends AuthRequestFactory { protected JustAuthProperties properties; @@ -69,15 +68,14 @@ public class YudaoAuthRequestFactory extends AuthRequestFactory { if (config == null) { return null; } - // 配置 http config - ReflectUtil.invoke(this, configureHttpConfigMethod, - authExtendSource.name(), config, properties.getHttpConfig()); + // 反射调用,配置 http config + ReflectUtil.invoke(this, configureHttpConfigMethod, authExtendSource.name(), config, properties.getHttpConfig()); // 获得拓展的 Request // noinspection SwitchStatementWithTooFewBranches switch (authExtendSource) { - case WECHAT_MINI_PROGRAM: - return new AuthWeChatMiniProgramRequest(config, authStateCache); + case WECHAT_MINI_APP: + return new AuthWeChatMiniAppRequest(config, authStateCache); default: return null; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/enums/AuthExtendSource.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/enums/AuthExtendSource.java index ce61bca40..f51c81e02 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/enums/AuthExtendSource.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/enums/AuthExtendSource.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.social.core.enums; import me.zhyd.oauth.config.AuthSource; /** - * 拓展JustAuth各api需要的url, 用枚举类分平台类型管理 + * 拓展 JustAuth 各 api 需要的 url, 用枚举类分平台类型管理 * * 默认配置 {@link me.zhyd.oauth.config.AuthDefaultSource} * @@ -14,25 +14,25 @@ public enum AuthExtendSource implements AuthSource { /** * 微信小程序授权登录 */ - WECHAT_MINI_PROGRAM { + WECHAT_MINI_APP { @Override public String authorize() { - // https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html - throw new UnsupportedOperationException("不支持获取授权url, 请使用小程序内置函数wx.login()登录获取code"); + // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 文档 + throw new UnsupportedOperationException("不支持获取授权 url,请使用小程序内置函数 wx.login() 登录获取 code"); } @Override public String accessToken() { - // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html - // 获取openid, unionid , session_key + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档 + // 获取 openid, unionId , session_key 等字段 return "https://api.weixin.qq.com/sns/jscode2session"; } @Override public String userInfo() { - //https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html - throw new UnsupportedOperationException("不支持获取用户信息url, 请使用小程序内置函数wx.getUserProfile()获取用户信息"); + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档 + throw new UnsupportedOperationException("不支持获取用户信息 url,请使用小程序内置函数 wx.getUserProfile() 获取用户信息"); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/model/AuthExtendToken.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/model/AuthExtendToken.java deleted file mode 100644 index 2c0f4f403..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/model/AuthExtendToken.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.framework.social.core.model; - -import lombok.*; -import me.zhyd.oauth.model.AuthToken; - -/** - * 授权所需的 token 拓展类 - * - * @author timfruit - * @date 2021-10-29 - */ -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class AuthExtendToken extends AuthToken { - - /** - * 微信小程序 - 会话密钥 - */ - private String miniSessionKey; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniProgramRequest.java b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniAppRequest.java similarity index 50% rename from yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniProgramRequest.java rename to yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniAppRequest.java index e5bbfcaad..5ff5b8578 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniProgramRequest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-social/src/main/java/cn/iocoder/yudao/framework/social/core/request/AuthWeChatMiniAppRequest.java @@ -1,100 +1,97 @@ -package cn.iocoder.yudao.framework.social.core.request; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.social.core.enums.AuthExtendSource; -import cn.iocoder.yudao.framework.social.core.model.AuthExtendToken; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; -import me.zhyd.oauth.cache.AuthStateCache; -import me.zhyd.oauth.config.AuthConfig; -import me.zhyd.oauth.exception.AuthException; -import me.zhyd.oauth.model.AuthCallback; -import me.zhyd.oauth.model.AuthToken; -import me.zhyd.oauth.model.AuthUser; -import me.zhyd.oauth.request.AuthDefaultRequest; -import me.zhyd.oauth.utils.HttpUtils; -import me.zhyd.oauth.utils.UrlBuilder; - -/** - * 微信小程序登陆 - * - * @author timfruit - * @date 2021-10-29 - */ -public class AuthWeChatMiniProgramRequest extends AuthDefaultRequest { - - public AuthWeChatMiniProgramRequest(AuthConfig config) { - super(config, AuthExtendSource.WECHAT_MINI_PROGRAM); - } - - public AuthWeChatMiniProgramRequest(AuthConfig config, AuthStateCache authStateCache) { - super(config, AuthExtendSource.WECHAT_MINI_PROGRAM, authStateCache); - } - - @Override - protected AuthToken getAccessToken(AuthCallback authCallback) { - // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html - String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())); - CodeSessionResponse accessTokenObject = JsonUtils.parseObject(response, CodeSessionResponse.class); - - this.checkResponse(accessTokenObject); - - AuthExtendToken token = new AuthExtendToken(); - token.setMiniSessionKey(accessTokenObject.sessionKey); - token.setOpenId(accessTokenObject.openid); - token.setUnionId(accessTokenObject.unionid); - return token; - } - - @Override - protected AuthUser getUserInfo(AuthToken authToken) { - // https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html - // 如果需要用户信息,需要在小程序调用函数后传给后端 - return AuthUser.builder() - .uuid(authToken.getOpenId()) - //TODO 是使用默认值,还是有小程序获取用户信息 和 code 一起传过来 - .nickname("") - .avatar("") - .token(authToken) - .source(source.toString()) - .build(); - } - - /** - * 检查响应内容是否正确 - * - * @param object 请求响应内容 - */ - private void checkResponse(CodeSessionResponse object) { - if (object.errcode != 0) { - throw new AuthException(object.errcode, object.errmsg); - } - } - - /** - * 返回获取 accessToken 的 url - * - * @param code 授权码 - * @return 返回获取 accessToken 的 url - */ - @Override - protected String accessTokenUrl(String code) { - return UrlBuilder.fromBaseUrl(source.accessToken()) - .queryParam("appid", config.getClientId()) - .queryParam("secret", config.getClientSecret()) - .queryParam("js_code", code) - .queryParam("grant_type", "authorization_code") - .build(); - } - - @Data - private static class CodeSessionResponse { - private int errcode; - private String errmsg; - @JsonProperty("session_key") - private String sessionKey; - private String openid; - private String unionid; - } - -} +package cn.iocoder.yudao.framework.social.core.request; + +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.social.core.enums.AuthExtendSource; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthDefaultRequest; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + * 微信小程序登陆 Request 请求 + * + * 由于 JustAuth 定位是面向 Web 为主的三方登录,所以微信小程序只能自己封装 + * + * @author timfruit + * @date 2021-10-29 + */ +public class AuthWeChatMiniAppRequest extends AuthDefaultRequest { + + public AuthWeChatMiniAppRequest(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthExtendSource.WECHAT_MINI_APP, authStateCache); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html 文档 + // 使用 code 获取对应的 openId、unionId 等字段 + String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(authCallback.getCode())); + JSCode2SessionResponse accessTokenObject = JsonUtils.parseObject(response, JSCode2SessionResponse.class); + assert accessTokenObject != null; + checkResponse(accessTokenObject); + // 拼装结果 + return AuthToken.builder() + .openId(accessTokenObject.getOpenid()) + .unionId(accessTokenObject.getUnionId()) + .build(); + } + + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + // 参见 https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html 文档 + // 如果需要用户信息,需要在小程序调用函数后传给后端 + return AuthUser.builder() + .username("") + .nickname("") + .avatar("") + .uuid(authToken.getOpenId()) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 检查响应内容是否正确 + * + * @param response 请求响应内容 + */ + private void checkResponse(JSCode2SessionResponse response) { + if (response.getErrorCode() != 0) { + throw new AuthException(response.getErrorCode(), response.getErrorMsg()); + } + } + + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("appid", config.getClientId()) + .queryParam("secret", config.getClientSecret()) + .queryParam("js_code", code) // 和父类不同,所以需要重写该方法 + .queryParam("grant_type", "authorization_code") + .build(); + } + + @Data + @SuppressWarnings("SpellCheckingInspection") + private static class JSCode2SessionResponse { + + @JsonProperty("errcode") + private int errorCode; + @JsonProperty("errmsg") + private String errorMsg; + @JsonProperty("session_key") + private String sessionKey; + private String openid; + @JsonProperty("unionid") + private String unionId; + + } + +} 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 763c722c4..4c53e2900 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-weixin/pom.xml @@ -33,9 +33,13 @@ com.github.binarywang - wx-java-mp-spring-boot-starter - 4.1.9.B + 4.3.4.B + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + 4.3.4.B diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/UploadRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/UploadRespVO.java new file mode 100644 index 000000000..3dc8b04e9 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/UploadRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(value = "管理后台 - 上传文件 VO") +public class UploadRespVO { + + @ApiModelProperty(value = "文件名", required = true, example = "yudao.jpg") + private String fileName; + + @ApiModelProperty(value = "文件 URL", required = true, example = "https://www.iocoder.cn/yudao.jpg") + private String fileUrl; +} diff --git a/yudao-module-mall/pom.xml b/yudao-module-mall/pom.xml new file mode 100644 index 000000000..866874852 --- /dev/null +++ b/yudao-module-mall/pom.xml @@ -0,0 +1,28 @@ + + + + yudao + cn.iocoder.boot + ${revision} + + 4.0.0 + + yudao-module-mall + pom + + ${project.artifactId} + + + market模块,主要实现营销相关功能 + 例如:营销活动、banner广告、优惠券、优惠码等功能。 + + + yudao-module-market-api + yudao-module-market-biz + yudao-module-product-api + yudao-module-product-biz + + + diff --git a/yudao-module-mall/yudao-module-market-api/pom.xml b/yudao-module-mall/yudao-module-market-api/pom.xml new file mode 100644 index 000000000..fef3428fc --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/pom.xml @@ -0,0 +1,26 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + 4.0.0 + yudao-module-market-api + jar + + ${project.artifactId} + + market 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java new file mode 100644 index 000000000..cb45004b6 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.market.api; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..d63b52465 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.market.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * market 错误码枚举类 + *

+ * market 系统,使用 1-003-000-000 段 + */ +public interface ErrorCodeConstants { + + + // ========== 促销活动相关 1003001000============ + ErrorCode ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "促销活动不存在"); + + // ========== banner相关 1003002000============ + ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner不存在"); +} 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 new file mode 100644 index 000000000..a02b0269c --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityStatusEnum.java @@ -0,0 +1,51 @@ +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 new file mode 100644 index 000000000..0413dba66 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/activity/MarketActivityTypeEnum.java @@ -0,0 +1,44 @@ +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-biz/pom.xml b/yudao-module-mall/yudao-module-market-biz/pom.xml new file mode 100644 index 000000000..e7fa33e9d --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/pom.xml @@ -0,0 +1,61 @@ + + + + cn.iocoder.boot + yudao-module-mall + ${revision} + + 4.0.0 + jar + yudao-module-market-biz + + ${project.artifactId} + + + market模块,主要实现营销相关功能 + 例如:营销活动、banner广告、优惠券、优惠码等功能。 + + + + + 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-weixin + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + yudao-spring-boot-starter-web + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + + + + 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 new file mode 100644 index 000000000..49b83b6c9 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/MarketTestController.java @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..dac4211a6 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/ActivityController.java @@ -0,0 +1,77 @@ +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 new file mode 100644 index 000000000..3ae5cd679 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityBaseVO.java @@ -0,0 +1,59 @@ +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 new file mode 100644 index 000000000..0ca112709 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityCreateReqVO.java @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..f3a4155b0 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityPageReqVO.java @@ -0,0 +1,77 @@ +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 beginStartTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束开始时间") + private Date endStartTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始结束时间") + private Date beginEndTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束结束时间") + private Date endEndTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始失效时间") + private Date beginInvalidTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束失效时间") + private Date endInvalidTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始删除时间") + private Date beginDeleteTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束删除时间") + private Date endDeleteTime; + + @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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityRespVO.java new file mode 100644 index 000000000..de7ca3af2 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.market.controller.admin.activity.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 促销活动 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ActivityRespVO extends ActivityBaseVO { + + @ApiModelProperty(value = "活动编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + 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 new file mode 100644 index 000000000..1db24f259 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/activity/vo/ActivityUpdateReqVO.java @@ -0,0 +1,18 @@ +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/banner/BannerController.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/BannerController.java new file mode 100644 index 000000000..932ead7de --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/BannerController.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.*; +import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.market.service.banner.BannerService; +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 = "管理后台 - Banner 管理") +@RestController +@RequestMapping("/market/banner") +@Validated +public class BannerController { + + @Resource + private BannerService bannerService; + + @PostMapping("/create") + @ApiOperation("创建 Banner") + @PreAuthorize("@ss.hasPermission('market:banner:create')") + public CommonResult createBanner(@Valid @RequestBody BannerCreateReqVO createReqVO) { + return success(bannerService.createBanner(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新 Banner") + @PreAuthorize("@ss.hasPermission('market:banner:update')") + public CommonResult updateBanner(@Valid @RequestBody BannerUpdateReqVO updateReqVO) { + bannerService.updateBanner(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除 Banner") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('market:banner:delete')") + public CommonResult deleteBanner(@RequestParam("id") Long id) { + bannerService.deleteBanner(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得 Banner") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('market:banner:query')") + public CommonResult getBanner(@RequestParam("id") Long id) { + BannerDO banner = bannerService.getBanner(id); + return success(BannerConvert.INSTANCE.convert(banner)); + } + + @GetMapping("/page") + @ApiOperation("获得 Banner 分页") + @PreAuthorize("@ss.hasPermission('market:banner:query')") + public CommonResult> getBannerPage(@Valid BannerPageReqVO pageVO) { + PageResult pageResult = bannerService.getBannerPage(pageVO); + return success(BannerConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java new file mode 100644 index 000000000..f5f37dbbc --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerBaseVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * Banner Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + * @author xia + */ +@Data +public class BannerBaseVO { + + @ApiModelProperty(value = "标题", required = true) + @NotNull(message = "标题不能为空") + private String title; + + @ApiModelProperty(value = "跳转链接", required = true) + @NotNull(message = "跳转链接不能为空") + private String url; + + @ApiModelProperty(value = "图片地址", required = true) + @NotNull(message = "图片地址不能为空") + private String picUrl; + + @ApiModelProperty(value = "排序", required = true) + @NotNull(message = "排序不能为空") + private Integer sort; + + @ApiModelProperty(value = "状态", required = true) + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @ApiModelProperty(value = "备注") + private String memo; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java new file mode 100644 index 000000000..0bb0c1bcf --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerCreateReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +/** + * @author xia + */ +@ApiModel("管理后台 - Banner 创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BannerCreateReqVO extends BannerBaseVO { + + + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java new file mode 100644 index 000000000..79a9c40c7 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerPageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +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; + +/** + * @author xia + */ +@ApiModel("管理后台 - Banner 分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BannerPageReqVO extends PageParam { + + @ApiModelProperty(value = "标题") + private String title; + + + @ApiModelProperty(value = "状态") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "开始创建时间") + private Date beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java new file mode 100644 index 000000000..f4045b991 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerRespVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * @author xia + */ +@ApiModel("管理后台 - Banner Response VO") +@Data +@ToString(callSuper = true) +public class BannerRespVO extends BannerBaseVO { + + @ApiModelProperty(value = "banner编号", required = true) + @NotNull(message = "banner编号不能为空") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java new file mode 100644 index 000000000..1f2d7ce80 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/admin/banner/vo/BannerUpdateReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.market.controller.admin.banner.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +/** + * @author xia + */ +@ApiModel("管理后台 - Banner更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BannerUpdateReqVO extends BannerBaseVO { + + @ApiModelProperty(value = "banner 编号", required = true) + @NotNull(message = "banner 编号不能为空") + private Long id; + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java new file mode 100644 index 000000000..7d45b87a9 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/AppMarketTestController.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.market.controller.app; + +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 = "用户 App - 营销") +@RestController +@RequestMapping("/market/test") +@Validated +public class AppMarketTestController { + + @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/app/banner/AppBannerController.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/banner/AppBannerController.java new file mode 100644 index 000000000..3d9d58eb8 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/controller/app/banner/AppBannerController.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.market.controller.app.banner; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerRespVO; +import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.market.service.banner.BannerService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * @author: XIA + */ +@RestController +@RequestMapping("/market/banner") +@Api(tags = "用户APP- 首页Banner") +@Validated +public class AppBannerController { + + // TODO @xia:使用 @Resource 哈 + @Autowired + private BannerService bannerService; + + // TODO @xia:新建一个 AppBannerRespVO,只返回必要的字段。status 要过滤下。然后 sort 下结果 + @GetMapping("/list") + @ApiOperation("获得banner列表") + @PreAuthorize("@ss.hasPermission('market:banner:query')") + public CommonResult> getBannerList() { + List list = bannerService.getBannerList(); + return success(BannerConvert.INSTANCE.convertList(list)); + } + +} 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 new file mode 100644 index 000000000..64ba73975 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/activity/ActivityConvert.java @@ -0,0 +1,32 @@ +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/banner/BannerConvert.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/banner/BannerConvert.java new file mode 100644 index 000000000..a78650f57 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/banner/BannerConvert.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.market.convert.banner; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerRespVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * Banner Convert + * + * @author xia + */ +@Mapper +public interface BannerConvert { + + BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class); + + + List convertList(List list); + + PageResult convertPage(PageResult pageResult); + + BannerRespVO convert(BannerDO banner); + + BannerDO convert(BannerCreateReqVO createReqVO); + + BannerDO convert(BannerUpdateReqVO updateReqVO); + +} 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 new file mode 100644 index 000000000..13dcbf67c --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/activity/ActivityDO.java @@ -0,0 +1,64 @@ +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/banner/BannerDO.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/banner/BannerDO.java new file mode 100644 index 000000000..1f48f7144 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/banner/BannerDO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.market.dal.dataobject.banner; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * banner DO + * + * @author xia + */ +@TableName("market_banner") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BannerDO extends BaseDO { + + /** + * 编号 + */ + private Long id; + /** + * 标题 + */ + private String title; + /** + * 跳转链接 + */ + private String url; + /** + * 图片链接 + */ + private String picUrl; + /** + * 排序 + */ + private Integer sort; + + /** + * 状态 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String memo; + + // TODO 芋艿 点击次数。&& 其他数据相关 + +} 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 new file mode 100644 index 000000000..b786ad981 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/activity/ActivityMapper.java @@ -0,0 +1,35 @@ +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.getBeginStartTime(), reqVO.getEndStartTime()) + .betweenIfPresent(ActivityDO::getEndTime, reqVO.getBeginEndTime(), reqVO.getEndEndTime()) + .betweenIfPresent(ActivityDO::getInvalidTime, reqVO.getBeginInvalidTime(), reqVO.getEndInvalidTime()) + .betweenIfPresent(ActivityDO::getDeleteTime, reqVO.getBeginDeleteTime(), reqVO.getEndDeleteTime()) + .eqIfPresent(ActivityDO::getTimeLimitedDiscount, reqVO.getTimeLimitedDiscount()) + .eqIfPresent(ActivityDO::getFullPrivilege, reqVO.getFullPrivilege()) + .betweenIfPresent(ActivityDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc(ActivityDO::getId)); + } + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java new file mode 100644 index 000000000..05c8cd3c9 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/mysql/banner/BannerMapper.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.market.dal.mysql.banner; + +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.market.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * Banner Mapper + * + * @author xia + */ +@Mapper +public interface BannerMapper extends BaseMapperX { + + default PageResult selectPage(BannerPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BannerDO::getTitle, reqVO.getTitle()) + .eqIfPresent(BannerDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BannerDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .betweenIfPresent(BannerDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc(BannerDO::getSort)); + } + +} 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 new file mode 100644 index 000000000..2efde4ec7 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/package-info.java @@ -0,0 +1,8 @@ +/** + * market 模块,我们放营销业务。 + * 例如说:营销活动、banner、优惠券等等 + * + * 1. Controller URL:以 /market/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 market_ 开头,方便在数据库中区分 + */ +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 new file mode 100644 index 000000000..1d5e27857 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityService.java @@ -0,0 +1,62 @@ +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 new file mode 100644 index 000000000..57bb9af53 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImpl.java @@ -0,0 +1,77 @@ +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/banner/BannerService.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerService.java new file mode 100644 index 000000000..67debdc3d --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerService.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.market.service.banner; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 首页 Banner Service 接口 + * + * @author xia + */ +public interface BannerService { + + /** + * 创建 Banner + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createBanner(@Valid BannerCreateReqVO createReqVO); + + /** + * 更新 Banner + * + * @param updateReqVO 更新信息 + */ + void updateBanner(@Valid BannerUpdateReqVO updateReqVO); + + /** + * 删除 Banner + * + * @param id 编号 + */ + void deleteBanner(Long id); + + /** + * 获得 Banner + * + * @param id 编号 + * @return Banner + */ + BannerDO getBanner(Long id); + + /** + * 获得所有 Banner列表 + * @return Banner列表 + */ + List getBannerList(); + + /** + * 获得 Banner 分页 + * + * @param pageReqVO 分页查询 + * @return Banner分页 + */ + PageResult getBannerPage(BannerPageReqVO pageReqVO); + +} diff --git a/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java new file mode 100644 index 000000000..04784ff66 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.market.service.banner; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerCreateReqVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerPageReqVO; +import cn.iocoder.yudao.module.market.controller.admin.banner.vo.BannerUpdateReqVO; +import cn.iocoder.yudao.module.market.convert.banner.BannerConvert; +import cn.iocoder.yudao.module.market.dal.dataobject.banner.BannerDO; +import cn.iocoder.yudao.module.market.dal.mysql.banner.BannerMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +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.market.enums.ErrorCodeConstants.BANNER_NOT_EXISTS; + +/** + * 首页banner 实现类 + * + * @author xia + */ +@Service +@Validated +public class BannerServiceImpl implements BannerService { + + @Resource + private BannerMapper bannerMapper; + + @Override + public Long createBanner(BannerCreateReqVO createReqVO) { + // 插入 + BannerDO banner = BannerConvert.INSTANCE.convert(createReqVO); + bannerMapper.insert(banner); + // 返回 + return banner.getId(); + } + + @Override + public void updateBanner(BannerUpdateReqVO updateReqVO) { + // 校验存在 + this.validateBannerExists(updateReqVO.getId()); + // 更新 + BannerDO updateObj = BannerConvert.INSTANCE.convert(updateReqVO); + bannerMapper.updateById(updateObj); + } + + @Override + public void deleteBanner(Long id) { + // 校验存在 + this.validateBannerExists(id); + // 删除 + bannerMapper.deleteById(id); + } + + private void validateBannerExists(Long id) { + if (bannerMapper.selectById(id) == null) { + throw exception(BANNER_NOT_EXISTS); + } + } + + @Override + public BannerDO getBanner(Long id) { + return bannerMapper.selectById(id); + } + + @Override + public List getBannerList() { + return bannerMapper.selectList(); + } + + @Override + public PageResult getBannerPage(BannerPageReqVO pageReqVO) { + return bannerMapper.selectPage(pageReqVO); + } + +} 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 new file mode 100644 index 000000000..f5b9a8b50 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/activity/ActivityServiceImplTest.java @@ -0,0 +1,202 @@ +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.setBeginStartTime(null); + reqVO.setEndStartTime(null); + reqVO.setBeginEndTime(null); + reqVO.setEndEndTime(null); + reqVO.setBeginInvalidTime(null); + reqVO.setEndInvalidTime(null); + reqVO.setBeginDeleteTime(null); + reqVO.setEndDeleteTime(null); + reqVO.setTimeLimitedDiscount(null); + reqVO.setFullPrivilege(null); + reqVO.setBeginCreateTime(null); + reqVO.setEndCreateTime(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-market-biz/src/test/resources/application-unit-test.yaml b/yudao-module-mall/yudao-module-market-biz/src/test/resources/application-unit-test.yaml new file mode 100644 index 000000000..60914d97f --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/test/resources/application-unit-test.yaml @@ -0,0 +1,49 @@ +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: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +# Resilience4j 配置项 + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao.module diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/logback.xml b/yudao-module-mall/yudao-module-market-biz/src/test/resources/logback.xml new file mode 100644 index 000000000..daf756bff --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql new file mode 100644 index 000000000..abad7c069 --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/clean.sql @@ -0,0 +1 @@ +DELETE FROM "market_activity"; diff --git a/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql new file mode 100644 index 000000000..3f3ce3c4d --- /dev/null +++ b/yudao-module-mall/yudao-module-market-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,19 @@ +CREATE TABLE IF NOT EXISTS "market_activity" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "title" varchar(50) NOT NULL, + "activity_type" tinyint(4) NOT NULL, + "status" tinyint(4) NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "invalid_time" datetime, + "delete_time" datetime, + "time_limited_discount" varchar(2000), + "full_privilege" varchar(2000), + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "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 '促销活动'; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-api/pom.xml b/yudao-module-mall/yudao-module-product-api/pom.xml new file mode 100644 index 000000000..7eb38008a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + cn.iocoder.boot + yudao-module-mall + ${revision} + + + yudao-module-product-api + jar + + ${project.artifactId} + + product 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.boot + yudao-common + + + + \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java new file mode 100644 index 000000000..b19092853 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.product.api; \ No newline at end of file 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 new file mode 100644 index 000000000..e328d2512 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.product.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * product 错误码枚举类 + *

+ * product 系统,使用 1-008-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 商品分类相关 1008001000============ + ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在"); + ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1008001001, "父分类不存在"); + ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001002, "存在子分类,无法删除"); + + // ========== 品牌相关编号 1008002000 ========== + ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在"); + + // ========== 规格名称 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不存在"); + + // ========== 商品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-biz/pom.xml b/yudao-module-mall/yudao-module-product-biz/pom.xml new file mode 100644 index 000000000..a06f8937c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + cn.iocoder.boot + yudao-module-mall + ${revision} + + + yudao-module-product-biz + jar + + ${project.artifactId} + + product 模块,主要实现商品相关功能 + 例如:品牌、商品分类、spu、sku等功能。 + + + + + + cn.iocoder.boot + yudao-module-product-api + ${revision} + + + + + 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 + + + + + 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-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/BrandController.java new file mode 100644 index 000000000..0702d206c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/BrandController.java @@ -0,0 +1,89 @@ +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 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.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/brand") +@Validated +public class BrandController { + + @Resource + private BrandService brandService; + + @PostMapping("/create") + @ApiOperation("创建品牌") + @PreAuthorize("@ss.hasPermission('product:brand:create')") + public CommonResult createBrand(@Valid @RequestBody BrandCreateReqVO createReqVO) { + return success(brandService.createBrand(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新品牌") + @PreAuthorize("@ss.hasPermission('product:brand:update')") + public CommonResult updateBrand(@Valid @RequestBody BrandUpdateReqVO updateReqVO) { + brandService.updateBrand(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除品牌") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:brand:delete')") + public CommonResult deleteBrand(@RequestParam("id") Long id) { + brandService.deleteBrand(id); + return success(true); + } + + @GetMapping("/get") + @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)); + } + + @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)); + } + + @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); + } + +} 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/BrandBaseVO.java new file mode 100644 index 000000000..57c5a390c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandBaseVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.product.controller.admin.brand.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 品牌 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class BrandBaseVO { + + @ApiModelProperty(value = "分类编号", required = true, example = "1") + @NotNull(message = "分类编号不能为空") + private Long categoryId; + + @ApiModelProperty(value = "品牌名称", required = true, example = "芋道") + @NotNull(message = "品牌名称不能为空") + private String name; + + @ApiModelProperty(value = "品牌图片", required = true) + @NotNull(message = "品牌图片不能为空") + private String bannerUrl; + + @ApiModelProperty(value = "品牌排序", example = "1") + private Integer sort; + + @ApiModelProperty(value = "品牌描述", example = "描述") + private String description; + + @ApiModelProperty(value = "状态", required = true, example = "0") + @NotNull(message = "状态不能为空") + private Integer status; + +} 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/BrandCreateReqVO.java new file mode 100644 index 000000000..3a6f844fb --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandCreateReqVO.java @@ -0,0 +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") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrandCreateReqVO extends BrandBaseVO { + +} 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 new file mode 100644 index 000000000..261b69ea5 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExcelVO.java @@ -0,0 +1,45 @@ +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 new file mode 100644 index 000000000..5fd90d7af --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandExportReqVO.java @@ -0,0 +1,32 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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 new file mode 100644 index 000000000..e4614eeb5 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandPageReqVO.java @@ -0,0 +1,34 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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/BrandRespVO.java new file mode 100644 index 000000000..5e010b4d0 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.admin.brand.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 品牌 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrandRespVO extends BrandBaseVO { + + @ApiModelProperty(value = "品牌编号", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + 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/BrandUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandUpdateReqVO.java new file mode 100644 index 000000000..287157f0e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/BrandUpdateReqVO.java @@ -0,0 +1,18 @@ +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") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrandUpdateReqVO extends BrandBaseVO { + + @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/category/CategoryController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java new file mode 100644 index 000000000..12408ac28 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/CategoryController.java @@ -0,0 +1,92 @@ +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/vo/CategoryBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java new file mode 100644 index 000000000..598e093eb --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryBaseVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.product.controller.admin.category.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** +* 商品分类 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class CategoryBaseVO { + + @ApiModelProperty(value = "父分类编号", required = true, example = "1") + @NotNull(message = "父分类编号不能为空") + private Long parentId; + + @ApiModelProperty(value = "分类名称", required = true, example = "办公文具") + @NotBlank(message = "分类名称不能为空") + private String name; + + @ApiModelProperty(value = "分类图标") + @NotBlank(message = "分类图标不能为空") + private String icon; + + @ApiModelProperty(value = "分类图片", required = true) + @NotBlank(message = "分类图片不能为空") + private String bannerUrl; + + @ApiModelProperty(value = "分类排序", required = true, example = "1") + private Integer sort; + + @ApiModelProperty(value = "分类描述", required = true, example = "描述") + private String description; + + @ApiModelProperty(value = "开启状态", required = true, example = "0") + @NotNull(message = "开启状态不能为空") + private Integer status; + +} 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/CategoryCreateReqVO.java new file mode 100644 index 000000000..ce583f08b --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryCreateReqVO.java @@ -0,0 +1,14 @@ +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 { + +} 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 new file mode 100644 index 000000000..f8e36ee8c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExcelVO.java @@ -0,0 +1,47 @@ +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 new file mode 100644 index 000000000..c1e23b3ed --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryExportReqVO.java @@ -0,0 +1,30 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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/category/vo/CategoryPageReqVO.java new file mode 100644 index 000000000..d824b8bd8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryPageReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.product.controller.admin.category.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; +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") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CategoryPageReqVO extends PageParam { + + @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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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/CategoryRespVO.java new file mode 100644 index 000000000..e7d0b2238 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.admin.category.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 商品分类 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CategoryRespVO extends CategoryBaseVO { + + @ApiModelProperty(value = "分类编号", required = true, example = "2") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + 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 new file mode 100644 index 000000000..12256efd3 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryTreeListReqVO.java @@ -0,0 +1,29 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; +} 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/CategoryUpdateReqVO.java new file mode 100644 index 000000000..13ee83c1e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/CategoryUpdateReqVO.java @@ -0,0 +1,18 @@ +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 CategoryUpdateReqVO extends CategoryBaseVO { + + @ApiModelProperty(value = "分类编号", required = true, example = "2") + @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/ProductPropertyController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java new file mode 100644 index 000000000..f1d31acab --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyController.java @@ -0,0 +1,97 @@ +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.module.product.service.property.ProductPropertyService; + +@Api(tags = "管理后台 - 规格名称") +@RestController +@RequestMapping("/product/property") +@Validated +public class ProductPropertyController { + + @Resource + private ProductPropertyService productPropertyService; + + @PostMapping("/create") + @ApiOperation("创建规格名称") + @PreAuthorize("@ss.hasPermission('product:property:create')") + public CommonResult createProperty(@Valid @RequestBody ProductPropertyCreateReqVO createReqVO) { + return success(productPropertyService.createProperty(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新规格名称") + @PreAuthorize("@ss.hasPermission('product:property:update')") + public CommonResult updateProperty(@Valid @RequestBody ProductPropertyUpdateReqVO updateReqVO) { + productPropertyService.updateProperty(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除规格名称") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:property:delete')") + public CommonResult deleteProperty(@RequestParam("id") Long id) { + productPropertyService.deleteProperty(id); + return success(true); + } + + @GetMapping("/get") + @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) { + 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) { + 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 new file mode 100644 index 000000000..4d38763f9 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyBaseVO.java @@ -0,0 +1,21 @@ +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/ProductPropertyCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyCreateReqVO.java new file mode 100644 index 000000000..54d72da8a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyCreateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo; + +import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO; +import lombok.*; +import io.swagger.annotations.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 规格名称创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyCreateReqVO extends ProductPropertyBaseVO { + + @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/ProductPropertyExcelVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExcelVO.java new file mode 100644 index 000000000..c935c1001 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExcelVO.java @@ -0,0 +1,29 @@ +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 new file mode 100644 index 000000000..e19ea2bfa --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyExportReqVO.java @@ -0,0 +1,29 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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/ProductPropertyPageReqVO.java new file mode 100644 index 000000000..34d7239f2 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyPageReqVO.java @@ -0,0 +1,31 @@ +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("管理后台 - 规格名称分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyPageReqVO extends PageParam { + + @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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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 new file mode 100644 index 000000000..978f26308 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyRespVO.java @@ -0,0 +1,23 @@ +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/ProductPropertyUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyUpdateReqVO.java new file mode 100644 index 000000000..ed190e8b0 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/vo/ProductPropertyUpdateReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.controller.admin.property.vo; + +import cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo.ProductPropertyValueCreateReqVO; +import lombok.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; +import java.util.List; + +@ApiModel("管理后台 - 规格名称更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyUpdateReqVO extends ProductPropertyBaseVO { + + @ApiModelProperty(value = "主键", required = true) + @NotNull(message = "主键不能为空") + private Long id; + + @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/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 new file mode 100644 index 000000000..ed600a9ac --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueBaseVO.java @@ -0,0 +1,24 @@ +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/propertyvalue/vo/ProductPropertyValueCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueCreateReqVO.java new file mode 100644 index 000000000..23ea0690c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; + +import lombok.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 规格值创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyValueCreateReqVO extends ProductPropertyValueBaseVO { + +} 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/propertyvalue/vo/ProductPropertyValueRespVO.java new file mode 100644 index 000000000..25fa25f02 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 规格值 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyValueRespVO extends ProductPropertyValueBaseVO { + + @ApiModelProperty(value = "主键", required = true) + private Long id; + + @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/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java new file mode 100644 index 000000000..894d6f6eb --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/propertyvalue/vo/ProductPropertyValueUpdateReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.product.controller.admin.propertyvalue.vo; + +import lombok.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 规格值更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductPropertyValueUpdateReqVO extends ProductPropertyValueBaseVO { + + @ApiModelProperty(value = "主键", required = true) + @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/sku/ProductSkuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java new file mode 100755 index 000000000..132d68d94 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java @@ -0,0 +1,99 @@ +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; + +@Api(tags = "管理后台 - 商品 sku") +@RestController +@RequestMapping("/product/sku") +@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 new file mode 100755 index 000000000..cefab9a47 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.product.controller.admin.sku.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 商品sku Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class ProductSkuBaseVO { + + // TODO @franky:example 要写哈; + + @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) + @NotNull(message = "销售价格,单位:分不能为空") + private Integer price; + + @ApiModelProperty(value = "原价, 单位: 分", required = true) + @NotNull(message = "原价, 单位: 分不能为空") + private Integer originalPrice; + + @ApiModelProperty(value = "成本价,单位: 分", required = true) + @NotNull(message = "成本价,单位: 分不能为空") + private Integer costPrice; + + @ApiModelProperty(value = "条形码", required = true) + @NotNull(message = "条形码不能为空") + private String barCode; + + @ApiModelProperty(value = "图片地址", required = true) + @NotNull(message = "图片地址不能为空") + private String picUrl; + + @ApiModelProperty(value = "状态: 0-正常 1-禁用") + private Integer status; + + // TODO @franky 要有 swagger 注解 + @Data + public static class Property { + @NotNull(message = "规格属性名id不能为空") + private Long propertyId; + @NotNull(message = "规格属性值id不能为空") + 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/ProductSkuCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateReqVO.java new file mode 100755 index 000000000..e01c272a4 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuCreateReqVO.java @@ -0,0 +1,14 @@ +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 ProductSkuCreateReqVO extends ProductSkuBaseVO { + +} 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 new file mode 100755 index 000000000..7caf1313d --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExcelVO.java @@ -0,0 +1,53 @@ +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 new file mode 100755 index 000000000..76847a28f --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuExportReqVO.java @@ -0,0 +1,47 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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 new file mode 100755 index 000000000..fcf8b95fc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuPageReqVO.java @@ -0,0 +1,49 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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 new file mode 100755 index 000000000..3b12ba21c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.product.controller.admin.sku.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 商品sku Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSkuRespVO extends ProductSkuBaseVO { + + @ApiModelProperty(value = "主键", required = true) + private Long id; + + @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/ProductSkuUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuUpdateReqVO.java new file mode 100755 index 000000000..984976eee --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuUpdateReqVO.java @@ -0,0 +1,18 @@ +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 new file mode 100755 index 000000000..71bfa82bd --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java @@ -0,0 +1,98 @@ +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.convert.spu.ProductSpuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; +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; + +@Api(tags = "管理后台 - 商品spu") +@RestController +@RequestMapping("/product/spu") +@Validated +public class ProductSpuController { + + @Resource + private ProductSpuService spuService; + + @PostMapping("/create") + @ApiOperation("创建商品spu") + @PreAuthorize("@ss.hasPermission('product:spu:create')") + public CommonResult createSpu(@Valid @RequestBody ProductSpuCreateReqVO createReqVO) { + return success(spuService.createSpu(createReqVO)); + } + + // TODO @franky:SpuUpdateReqVO 缺少前缀 + @PutMapping("/update") + @ApiOperation("更新商品spu") + @PreAuthorize("@ss.hasPermission('product:spu:update')") + public CommonResult updateSpu(@Valid @RequestBody SpuUpdateReqVO updateReqVO) { + spuService.updateSpu(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除商品spu") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:spu:delete')") + public CommonResult deleteSpu(@RequestParam("id") Long id) { + spuService.deleteSpu(id); + return success(true); + } + + @GetMapping("/get") + @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) { + return success(spuService.getSpu(id)); + } + + @GetMapping("/list") + @ApiOperation("获得商品spu列表") + @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('product:spu:query')") + public CommonResult> getSpuList(@RequestParam("ids") Collection ids) { + List list = spuService.getSpuList(ids); + return success(ProductSpuConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @ApiOperation("获得商品spu分页") + @PreAuthorize("@ss.hasPermission('product:spu:query')") + public CommonResult> getSpuPage(@Valid SpuPageReqVO 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 new file mode 100755 index 000000000..65a5f2a0e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 商品spu Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class ProductSpuBaseVO { + + @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 Boolean status; + +} 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 new file mode 100755 index 000000000..6281e20d5 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.product.controller.admin.spu.vo; + +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateReqVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import java.util.List; + +@ApiModel("管理后台 - 商品spu创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ProductSpuCreateReqVO extends ProductSpuBaseVO { + + @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/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 new file mode 100755 index 000000000..9e90acd3d --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExcelVO.java @@ -0,0 +1,53 @@ +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 new file mode 100755 index 000000000..c5658a67e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuExportReqVO.java @@ -0,0 +1,53 @@ +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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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/SpuPageReqVO.java new file mode 100755 index 000000000..59337a242 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuPageReqVO.java @@ -0,0 +1,55 @@ +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("管理后台 - 商品spu分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SpuPageReqVO extends PageParam { + + @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 String 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 beginCreateTime; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ApiModelProperty(value = "结束创建时间") + private Date endCreateTime; + +} 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 new file mode 100755 index 000000000..90387f90a --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuRespVO.java @@ -0,0 +1,36 @@ +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 new file mode 100755 index 000000000..9e4092235 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/SpuUpdateReqVO.java @@ -0,0 +1,25 @@ +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/convert/brand/BrandConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/BrandConvert.java new file mode 100644 index 000000000..316515603 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/brand/BrandConvert.java @@ -0,0 +1,34 @@ +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/category/CategoryConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java new file mode 100644 index 000000000..2b2cfe99c --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/category/CategoryConvert.java @@ -0,0 +1,34 @@ +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/property/ProductPropertyConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java new file mode 100644 index 000000000..c44cdc841 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyConvert.java @@ -0,0 +1,34 @@ +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.dal.dataobject.property.ProductPropertyDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; + +/** + * 规格名称 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface ProductPropertyConvert { + + ProductPropertyConvert INSTANCE = Mappers.getMapper(ProductPropertyConvert.class); + + ProductPropertyDO convert(ProductPropertyCreateReqVO bean); + + ProductPropertyDO convert(ProductPropertyUpdateReqVO bean); + + ProductPropertyRespVO convert(ProductPropertyDO 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/propertyvalue/ProductPropertyValueConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java new file mode 100644 index 000000000..447b9a5ea --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.product.convert.propertyvalue; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +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 + * + * @author 芋道源码 + */ +@Mapper +public interface ProductPropertyValueConvert { + + ProductPropertyValueConvert INSTANCE = Mappers.getMapper(ProductPropertyValueConvert.class); + + ProductPropertyValueDO convert(ProductPropertyValueCreateReqVO bean); + + ProductPropertyValueDO convert(ProductPropertyValueUpdateReqVO bean); + + ProductPropertyValueRespVO convert(ProductPropertyValueDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList03(List list); + +} 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 new file mode 100755 index 000000000..232ce3178 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java @@ -0,0 +1,60 @@ +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 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; + +/** + * 商品sku Convert + * + * @author 芋道源码 + */ +@Mapper +public interface ProductSkuConvert { + + ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class); + + @Mapping(source = "properties", target = "properties", qualifiedByName = "translateStringFromList") + ProductSkuDO convert(ProductSkuCreateReqVO bean); + + @Mapping(source = "properties", target = "properties", qualifiedByName = "translateStringFromList") + ProductSkuDO convert(ProductSkuUpdateReqVO bean); + + @Mapping(source = "properties", target = "properties", qualifiedByName = "tokenizeToBeanArray") + ProductSkuRespVO convert(ProductSkuDO bean); + + @Mapping(source = "properties", target = "properties", qualifiedByName = "tokenizeToExcelBeanArray") + ProductSkuExcelVO convertToExcelVO(ProductSkuDO bean); + + List convertList(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 new file mode 100755 index 000000000..92efaf0e8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java @@ -0,0 +1,52 @@ +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.dal.dataobject.spu.ProductSpuDO; +import org.springframework.util.StringUtils; + +/** + * 商品spu Convert + * + * @author 芋道源码 + */ +@Mapper +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); + + @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray") + SpuRespVO convert(ProductSpuDO bean); + + @Mapping(source = "picUrls", target = "picUrls", qualifiedByName = "tokenizeToStringArray") + SpuExcelVO convertToExcelVO(ProductSpuDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List 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/BrandDO.java new file mode 100644 index 000000000..2cbcee877 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/brand/BrandDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.brand; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +// TODO @JeromeSoar:Product 前缀 +/** + * 品牌 DO + * + * @author 芋道源码 + */ +@TableName("product_brand") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BrandDO extends BaseDO { + + /** + * 品牌编号 + */ + @TableId + private Long id; + /** + * 分类编号 + */ + private Long categoryId; + /** + * 品牌名称 + */ + private String name; + /** + * 品牌图片 + */ + private String bannerUrl; + /** + * 品牌排序 + */ + private Integer sort; + /** + * 品牌描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@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/category/CategoryDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/CategoryDO.java new file mode 100644 index 000000000..c6eb655ab --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/category/CategoryDO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.category; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +// TODO @JeromeSoar:Product 前缀 +/** + * 商品分类 DO + * + * @author 芋道源码 + */ +@TableName("product_category") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CategoryDO extends BaseDO { + + /** + * 分类编号 + */ + @TableId + private Long id; + /** + * 父分类编号 + */ + private Long parentId; + /** + * 分类名称 + */ + private String name; + /** + * 分类图标 + */ + private String icon; + /** + * 分类 Banner 图片 + * + * 第一层的商品分类,会有该字段,用于用户 App 展示 + */ + private String bannerUrl; + /** + * 分类排序 + */ + private Integer sort; + /** + * 分类描述 + */ + private String description; + /** + * 开启状态 + * + * 枚举 {@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/property/ProductPropertyDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java new file mode 100644 index 000000000..5255c36ed --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyDO.java @@ -0,0 +1,41 @@ +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; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 规格名称 DO + * + * @author 芋道源码 + */ +@TableName("product_property") +@KeySequence("product_property_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductPropertyDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 规格名称 + */ + private String name; + /** + * 状态: 0 开启 ,1 禁用 + * + * {@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/property/ProductPropertyValueDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java new file mode 100644 index 000000000..0203939cc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/property/ProductPropertyValueDO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.property; + +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_property_value") +@KeySequence("product_property_value_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductPropertyValueDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 规格键 id + * + * TODO @franky:加个 关联 {@link ProductPropertyDO#getId()} ,这样就能更好的知道 + */ + private Long propertyId; + /** + * 规格值名字 + */ + private String name; + /** + * 状态: 1 开启 ,2 禁用 + */ + private Integer status; + +} 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 new file mode 100755 index 000000000..1ffaaeef2 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.sku; + +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.*; + +/** + * 商品sku DO + * + * @author 芋道源码 + */ +@TableName("product_sku") +@KeySequence("product_sku_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductSkuDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * spu编号 + */ + private Long spuId; + /** + * 规格值数组-json格式, [{propertId: , valueId: }, {propertId: , valueId: }] + */ + // TODO franky:可以定义一个内部的 Property 类,然后 List + private String properties; + /** + * 销售价格,单位:分 + */ + private Integer price; + /** + * 原价,单位:分 + */ + private Integer originalPrice; + /** + * 成本价,单位: 分 + */ + private Integer costPrice; + /** + * 条形码 + */ + private String barCode; + /** + * 图片地址 + */ + private String picUrl; + /** + * 状态: 0-正常 1-禁用 + */ + private Integer status; + +} + 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 new file mode 100755 index 000000000..2da638077 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.product.dal.dataobject.spu; + +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("product_spu") +@KeySequence("product_spu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProductSpuDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 商品名称 + */ + private String name; + /** + * 卖点 + */ + private String sellPoint; + /** + * 描述 + */ + private String description; + /** + * 分类id + */ + private Long categoryId; + /** + * 商品主图地址,* 数组,以逗号分隔,最多上传15张 + */ + // TODO franky:List。可以参考别的模块,怎么处理这种类型的哈 + private String picUrls; + /** + * 排序字段 + */ + private Integer sort; + /** + * 点赞初始人数 + */ + private Integer likeCount; + /** + * 价格 单位使用:分 + */ + private Integer price; + /** + * 库存数量 + */ + private Integer quantity; + /** + * 上下架状态: 0 上架(开启) 1 下架(禁用) + */ + private Boolean status; + +} 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 new file mode 100644 index 000000000..84fda1c32 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/brand/BrandMapper.java @@ -0,0 +1,38 @@ +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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc(BrandDO::getId)); + } + +} 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 new file mode 100644 index 000000000..a6ea75b2e --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/category/CategoryMapper.java @@ -0,0 +1,40 @@ +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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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/property/ProductPropertyMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java new file mode 100644 index 000000000..079b21c84 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/property/ProductPropertyMapper.java @@ -0,0 +1,36 @@ +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.module.product.dal.dataobject.property.ProductPropertyDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.product.controller.admin.property.vo.*; + +/** + * 规格名称 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ProductPropertyMapper extends BaseMapperX { + + default PageResult selectPage(ProductPropertyPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ProductPropertyDO::getName, reqVO.getName()) + .eqIfPresent(ProductPropertyDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(ProductPropertyDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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/propertyvalue/ProductPropertyValueMapper.java new file mode 100644 index 000000000..5c11a9f42 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/propertyvalue/ProductPropertyValueMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.product.dal.mysql.propertyvalue; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 规格值 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ProductPropertyValueMapper extends BaseMapperX { + + // TODO @franky:方法名,selectListByXXX。mapper 的操作都是 crud + default List getPropertyValueListByPropertyId(List propertyIds){ + // TODO @franky:调用父类的 selectList + return selectList(new LambdaQueryWrapperX() + .inIfPresent(ProductPropertyValueDO::getPropertyId, propertyIds)); + } + + default void deletePropertyValueByPropertyId(Long propertyId){ + // TODO @franky:delete(new ) 即可 + LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); + queryWrapperX.eq(ProductPropertyValueDO::getPropertyId, propertyId) + .eq(ProductPropertyValueDO::getDeleted, false); + delete(queryWrapperX); + } +} 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 new file mode 100755 index 000000000..5b856fd96 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java @@ -0,0 +1,61 @@ +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 + * + * @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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc(ProductSkuDO::getId)); + } + + // TODO @franky:方法名 selectList; 可以直接调用 selectList + default List selectBySpuIds(List spuIds) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(ProductSkuDO::getSpuId, spuIds)); + } + + default void deleteBySpuId(Long spuId) { + // TODO @franky:直接 delete(new XXX) 即可,更简洁一些 + LambdaQueryWrapperX lambdaQueryWrapperX = new LambdaQueryWrapperX() + .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 new file mode 100755 index 000000000..d6349eb2f --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java @@ -0,0 +1,52 @@ +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.module.product.dal.dataobject.spu.ProductSpuDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; + +/** + * 商品spu Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ProductSpuMapper extends BaseMapperX { + + default PageResult selectPage(SpuPageReqVO 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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .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.getBeginCreateTime(), reqVO.getEndCreateTime()) + .orderByDesc(ProductSpuDO::getId)); + } + +} 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 new file mode 100644 index 000000000..4e80cc2bc --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java @@ -0,0 +1,7 @@ +/** + * TODO + * + * @author JeromeSoar + * @since 2022-04-24 + */ +package cn.iocoder.yudao.module.product; \ No newline at end of file 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/BrandService.java new file mode 100644 index 000000000..2cb356fed --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandService.java @@ -0,0 +1,70 @@ +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; + +/** + * 品牌 Service 接口 + * + * @author 芋道源码 + */ +public interface BrandService { + + /** + * 创建品牌 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createBrand(@Valid BrandCreateReqVO createReqVO); + + /** + * 更新品牌 + * + * @param updateReqVO 更新信息 + */ + void updateBrand(@Valid BrandUpdateReqVO updateReqVO); + + /** + * 删除品牌 + * + * @param id 编号 + */ + void deleteBrand(Long id); + + /** + * 获得品牌 + * + * @param id 编号 + * @return 品牌 + */ + BrandDO getBrand(Long id); + + /** + * 获得品牌列表 + * + * @param ids 编号 + * @return 品牌列表 + */ + List getBrandList(Collection ids); + + /** + * 获得品牌分页 + * + * @param pageReqVO 分页查询 + * @return 品牌分页 + */ + PageResult getBrandPage(BrandPageReqVO pageReqVO); + + /** + * 获得品牌列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 品牌列表 + */ + List getBrandList(BrandExportReqVO exportReqVO); + +} 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 new file mode 100644 index 000000000..dedda3ae2 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImpl.java @@ -0,0 +1,82 @@ +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/category/CategoryService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java new file mode 100644 index 000000000..4df2f19f9 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryService.java @@ -0,0 +1,86 @@ +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 new file mode 100644 index 000000000..3fa441b66 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImpl.java @@ -0,0 +1,109 @@ +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/property/ProductPropertyService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java new file mode 100644 index 000000000..a663a49ce --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyService.java @@ -0,0 +1,85 @@ +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; + +/** + * 规格名称 Service 接口 + * + * @author 芋道源码 + */ +public interface ProductPropertyService { + + /** + * 创建规格名称 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProperty(@Valid ProductPropertyCreateReqVO createReqVO); + + /** + * 更新规格名称 + * + * @param updateReqVO 更新信息 + */ + void updateProperty(@Valid ProductPropertyUpdateReqVO updateReqVO); + + /** + * 删除规格名称 + * + * @param id 编号 + */ + void deleteProperty(Long id); + + /** + * 获得规格名称 + * + * @param id 编号 + * @return 规格名称 + */ + ProductPropertyDO getProperty(Long id); + + /** + * 获得规格名称列表 + * + * @param ids 编号 + * @return 规格名称列表 + */ + List getPropertyList(Collection ids); + + /** + * 获得规格名称分页 + * + * @param pageReqVO 分页查询 + * @return 规格名称分页 + */ + PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO); + + /** + * 获得规格名称列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 规格名称列表 + */ + List getPropertyList(ProductPropertyExportReqVO exportReqVO); + + /** + * 获取属性及属性值列表 分页 + * @param pageReqVO + * @return + */ + PageResult getPropertyListPage(ProductPropertyPageReqVO pageReqVO); + + ProductPropertyRespVO getPropertyResp(Long id); + + /** + * 根据数据名id集合查询属性名以及属性值的集合 + * @param propertyIds 属性名id集合 + * @return + */ + List selectByIds(List propertyIds); +} 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 new file mode 100644 index 000000000..9cb1a0da3 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java @@ -0,0 +1,155 @@ +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.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 org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +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; + +/** + * 规格名称 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ProductPropertyServiceImpl implements ProductPropertyService { + + @Resource + private ProductPropertyMapper productPropertyMapper; + + @Resource + private ProductPropertyValueMapper productPropertyValueMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createProperty(ProductPropertyCreateReqVO createReqVO) { + // 插入 + ProductPropertyDO property = ProductPropertyConvert.INSTANCE.convert(createReqVO); + productPropertyMapper.insert(property); + + //插入属性值 + List propertyValueList = createReqVO.getPropertyValueList(); + List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); + productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(property.getId())); + productPropertyValueMapper.insertBatch(productPropertyValueDOList); + // 返回 + return property.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateProperty(ProductPropertyUpdateReqVO updateReqVO) { + // 校验存在 + this.validatePropertyExists(updateReqVO.getId()); + // 更新 + ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO); + productPropertyMapper.updateById(updateObj); + //更新属性值,先删后加 + productPropertyValueMapper.deletePropertyValueByPropertyId(updateReqVO.getId()); + List propertyValueList = updateReqVO.getPropertyValueList(); + List productPropertyValueDOList = ProductPropertyValueConvert.INSTANCE.convertList03(propertyValueList); + productPropertyValueDOList.stream().forEach(x-> x.setPropertyId(updateReqVO.getId())); + productPropertyValueMapper.insertBatch(productPropertyValueDOList); + } + + @Override + public void deleteProperty(Long id) { + // 校验存在 + this.validatePropertyExists(id); + // 删除 + productPropertyMapper.deleteById(id); + //同步删除属性值 + productPropertyValueMapper.deletePropertyValueByPropertyId(id); + } + + private void validatePropertyExists(Long id) { + if (productPropertyMapper.selectById(id) == null) { + throw exception(PROPERTY_NOT_EXISTS); + } + } + + @Override + public ProductPropertyDO getProperty(Long id) { + return productPropertyMapper.selectById(id); + } + + @Override + public List getPropertyList(Collection ids) { + return productPropertyMapper.selectBatchIds(ids); + } + + @Override + public PageResult getPropertyPage(ProductPropertyPageReqVO pageReqVO) { + return productPropertyMapper.selectPage(pageReqVO); + } + + @Override + public List getPropertyList(ProductPropertyExportReqVO exportReqVO) { + return productPropertyMapper.selectList(exportReqVO); + } + + @Override + 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()); + + //获取属性值列表 + List productPropertyValueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); + List propertyValueRespVOList = ProductPropertyValueConvert.INSTANCE.convertList(productPropertyValueDOList); + //组装一对多 + propertyRespVOPageResult.getList().forEach(x->{ + Long propertyId = x.getId(); + List valueDOList = propertyValueRespVOList.stream().filter(v -> v.getPropertyId().equals(propertyId)).collect(Collectors.toList()); + x.setPropertyValueList(valueDOList); + }); + return propertyRespVOPageResult; + } + + private List getPropertyValueListByPropertyId(List propertyIds) { + return productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); + } + + @Override + public ProductPropertyRespVO getPropertyResp(Long id) { + //查询规格 + ProductPropertyDO property = getProperty(id); + ProductPropertyRespVO propertyRespVO = ProductPropertyConvert.INSTANCE.convert(property); + //查询属性值 + List valueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(Arrays.asList(id)); + List propertyValueRespVOS = ProductPropertyValueConvert.INSTANCE.convertList(valueDOList); + //组装 + propertyRespVO.setPropertyValueList(propertyValueRespVOS); + return propertyRespVO; + } + + @Override + public List selectByIds(List propertyIds) { + List productPropertyRespVO = ProductPropertyConvert.INSTANCE.convertList(productPropertyMapper.selectBatchIds(propertyIds)); + //查询属性值 + List valueDOList = productPropertyValueMapper.getPropertyValueListByPropertyId(propertyIds); + Map> propertyValuesMap = valueDOList.stream().collect(Collectors.groupingBy(ProductPropertyValueDO::getPropertyId)); + productPropertyRespVO.forEach(p -> p.setPropertyValueList(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 new file mode 100755 index 000000000..0396506ef --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java @@ -0,0 +1,119 @@ +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.dal.dataobject.sku.ProductSkuDO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * 商品sku Service 接口 + * + * @author 芋道源码 + */ +public interface ProductSkuService { + + /** + * 创建商品sku + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSku(@Valid ProductSkuCreateReqVO createReqVO); + + /** + * 更新商品sku + * + * @param updateReqVO 更新信息 + */ + void updateSku(@Valid ProductSkuUpdateReqVO updateReqVO); + + /** + * 删除商品sku + * + * @param id 编号 + */ + void deleteSku(Long id); + + /** + * 获得商品sku + * + * @param id 编号 + * @return 商品sku + */ + ProductSkuDO getSku(Long id); + + /** + * 获得商品sku列表 + * + * @param ids 编号 + * @return 商品sku列表 + */ + 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); + + /** + * 批量保存 sku + * + * @param list sku对象集合 + */ + void createSkus(List list); + + /** + * 获得商品 sku 集合 + * + * @param spuId spu 编号 + * @return 商品sku 集合 + */ + List getSkusBySpuId(Long spuId); + + /** + * 获得 spu 对应的 sku 集合 + * + * @param spuIds spu 编码集合 + * @return 商品 sku 集合 + */ + List getSkusBySpuIds(List spuIds); + + /** + * 通过 spuId 删除 sku 信息 + * + * @param spuId spu 编码 + */ + 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 new file mode 100755 index 000000000..91a60929b --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java @@ -0,0 +1,185 @@ +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.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.service.property.ProductPropertyService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +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; + +/** + * 商品sku Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ProductSkuServiceImpl implements ProductSkuService { + + @Resource + private ProductSkuMapper productSkuMapper; + + @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) { + // 校验存在 + this.validateSkuExists(id); + // 删除 + productSkuMapper.deleteById(id); + } + + private void validateSkuExists(Long id) { + if (productSkuMapper.selectById(id) == null) { + throw exception(SKU_NOT_EXISTS); + } + } + + @Override + public ProductSkuDO getSku(Long id) { + return productSkuMapper.selectById(id); + } + + @Override + public List getSkuList(Collection ids) { + return productSkuMapper.selectBatchIds(ids); + } + + @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); + } + 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); + } + }); + // 校验是否有重复的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); + } + }); + } + + @Override + public void createSkus(List skuDOList) { + productSkuMapper.insertBatch(skuDOList); + } + + @Override + public List getSkusBySpuId(Long spuId) { + return productSkuMapper.selectBySpuIds(Collections.singletonList(spuId)); + } + + @Override + public List getSkusBySpuIds(List spuIds) { + return productSkuMapper.selectBySpuIds(spuIds); + } + + @Override + public void deleteSkuBySpuId(Long spuId) { + productSkuMapper.deleteBySpuId(spuId); + } + + @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)); + + // 拆分三个集合, 新插入的, 需要更新的,需要删除的 + List insertSkus = new ArrayList<>(); + List updateSkus = new ArrayList<>(); + List deleteSkus = new ArrayList<>(); + + // TODO @芋艿:是不是基于规格匹配会比较好。 + allUpdateSkus.forEach(p -> { + if (null != p.getId()) { + if (existsSkuMap.get(p.getId()) != null) { + updateSkus.add(p); + return; + } + deleteSkus.add(p); + return; + } + p.setSpuId(spuId); + insertSkus.add(p); + }); + + if (insertSkus.size() > 0) { + 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 new file mode 100755 index 000000000..e4fc47d56 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java @@ -0,0 +1,70 @@ +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; + +/** + * 商品spu Service 接口 + * + * @author 芋道源码 + */ +public interface ProductSpuService { + + /** + * 创建商品spu + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSpu(@Valid ProductSpuCreateReqVO createReqVO); + + /** + * 更新商品spu + * + * @param updateReqVO 更新信息 + */ + void updateSpu(@Valid SpuUpdateReqVO updateReqVO); + + /** + * 删除商品spu + * + * @param id 编号 + */ + void deleteSpu(Long id); + + /** + * 获得商品spu + * + * @param id 编号 + * @return 商品spu + */ + SpuRespVO getSpu(Long id); + + /** + * 获得商品spu列表 + * + * @param ids 编号 + * @return 商品spu列表 + */ + List getSpuList(Collection ids); + + /** + * 获得商品spu分页 + * + * @param pageReqVO 分页查询 + * @return 商品spu分页 + */ + PageResult getSpuPage(SpuPageReqVO pageReqVO); + + /** + * 获得商品spu列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 商品spu列表 + */ + List getSpuList(SpuExportReqVO exportReqVO); + +} 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 new file mode 100755 index 000000000..4420b0af8 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java @@ -0,0 +1,147 @@ +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.module.product.controller.admin.sku.vo.ProductSkuRespVO; +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +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.service.sku.ProductSkuService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +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.SPU_NOT_EXISTS; + +/** + * 商品spu Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ProductSpuServiceImpl implements ProductSpuService { + + @Resource + private ProductSpuMapper ProductSpuMapper; + + @Resource + private CategoryService categoryService; + + @Resource + private ProductSkuService productSkuService; + + @Override + @Transactional + public Long createSpu(ProductSpuCreateReqVO createReqVO) { + // 校验分类 + categoryService.validatedCategoryById(createReqVO.getCategoryId()); + // 校验SKU + List skuCreateReqList = createReqVO.getSkus(); + productSkuService.validateSkus(skuCreateReqList); + // 插入SPU + ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); + ProductSpuMapper.insert(spu); + // sku关联SPU属性 + skuCreateReqList.forEach(p -> { + p.setSpuId(spu.getId()); + }); + List skuDOList = ProductSkuConvert.INSTANCE.convertSkuDOList(skuCreateReqList); + // 批量插入sku + productSkuService.createSkus(skuDOList); + // 返回 + return spu.getId(); + } + + @Override + @Transactional + public void updateSpu(SpuUpdateReqVO updateReqVO) { + // 校验 spu 是否存在 + this.validateSpuExists(updateReqVO.getId()); + // 校验分类 + categoryService.validatedCategoryById(updateReqVO.getCategoryId()); + // 校验SKU + List skuCreateReqList = updateReqVO.getSkus(); + productSkuService.validateSkus(skuCreateReqList); + // 更新 + ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); + ProductSpuMapper.updateById(updateObj); + // 更新 sku + productSkuService.updateSkus(updateObj.getId(), updateReqVO.getSkus()); + } + + @Override + @Transactional + public void deleteSpu(Long id) { + // 校验存在 + this.validateSpuExists(id); + // 删除 SPU + ProductSpuMapper.deleteById(id); + // 删除关联的 SKU + productSkuService.deleteSkuBySpuId(id); + } + + private void validateSpuExists(Long id) { + if (ProductSpuMapper.selectById(id) == null) { + throw exception(SPU_NOT_EXISTS); + } + } + + @Override + public SpuRespVO getSpu(Long id) { + ProductSpuDO spu = ProductSpuMapper.selectById(id); + SpuRespVO spuVO = ProductSpuConvert.INSTANCE.convert(spu); + if (null != spuVO) { + List skuReqs = ProductSkuConvert.INSTANCE.convertList(productSkuService.getSkusBySpuId(id)); + spuVO.setSkus(skuReqs); + // 组合分类 + if (null != spuVO.getCategoryId()) { + LinkedList categoryArray = new LinkedList<>(); + Long parentId = spuVO.getCategoryId(); + categoryArray.addFirst(parentId); + while (parentId != 0) { + parentId = categoryService.getCategory(parentId).getParentId(); + if (parentId > 0) { + categoryArray.addFirst(parentId); + } + } + spuVO.setCategoryIds(categoryArray); + } + } + return spuVO; + } + + @Override + public List getSpuList(Collection ids) { + return ProductSpuMapper.selectBatchIds(ids); + } + + @Override + public PageResult getSpuPage(SpuPageReqVO pageReqVO) { + PageResult spuVOs = ProductSpuConvert.INSTANCE.convertPage(ProductSpuMapper.selectPage(pageReqVO)); + // 查询 sku 的信息 + List spuIds = spuVOs.getList().stream().map(SpuRespVO::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()))); + return spuVOs; + } + + @Override + public List getSpuList(SpuExportReqVO exportReqVO) { + return ProductSpuMapper.selectList(exportReqVO); + } + +} 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 new file mode 100644 index 000000000..eb1a07cfa --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/brand/BrandServiceImplTest.java @@ -0,0 +1,175 @@ +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.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + 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.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + 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/category/CategoryServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImplTest.java new file mode 100644 index 000000000..5749e65ee --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/CategoryServiceImplTest.java @@ -0,0 +1,194 @@ +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.setBeginCreateTime(null); + reqVO.setEndCreateTime(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.setBeginCreateTime(null); + reqVO.setEndCreateTime(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/sku/SkuServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java new file mode 100755 index 000000000..4253b6fd0 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/SkuServiceImplTest.java @@ -0,0 +1,215 @@ +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 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 ProductSkuServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(ProductSkuServiceImpl.class) +public class SkuServiceImplTest extends BaseDbUnitTest { + + @Resource + private ProductSkuServiceImpl ProductSkuService; + + @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 数据 + ProductSkuDO dbSku = randomPojo(ProductSkuDO.class); + ProductSkuMapper.insert(dbSku);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSku.getId(); + + // 调用 + ProductSkuService.deleteSku(id); + // 校验数据不存在了 + assertNull(ProductSkuMapper.selectById(id)); + } + + @Test + public void testDeleteSku_notExists() { + // 准备参数 + Long id = 1L; + + // 调用, 并断言异常 + 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.setBeginCreateTime(null); + reqVO.setEndCreateTime(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.setBeginCreateTime(null); + reqVO.setEndCreateTime(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 new file mode 100755 index 000000000..6e943f234 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java @@ -0,0 +1,231 @@ +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.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; +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 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 ProductSpuServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(ProductSpuServiceImpl.class) +public class ProductSpuServiceImplTest extends BaseDbUnitTest { + + @Resource + private ProductSpuServiceImpl spuService; + + @Resource + private ProductSpuMapper ProductSpuMapper; + + @Test + public void testCreateSpu_success() { + // 准备参数 + ProductSpuCreateReqVO reqVO = randomPojo(ProductSpuCreateReqVO.class); + + // 调用 + Long spuId = spuService.createSpu(reqVO); + // 断言 + assertNotNull(spuId); + // 校验记录的属性是否正确 + ProductSpuDO spu = ProductSpuMapper.selectById(spuId); + assertPojoEquals(reqVO, spu); + } + + @Test + public void testUpdateSpu_success() { + // mock 数据 + ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class); + ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SpuUpdateReqVO reqVO = randomPojo(SpuUpdateReqVO.class, o -> { + o.setId(dbSpu.getId()); // 设置更新的 ID + }); + + // 调用 + spuService.updateSpu(reqVO); + // 校验是否更新正确 + ProductSpuDO spu = ProductSpuMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, spu); + } + + @Test + public void testUpdateSpu_notExists() { + // 准备参数 + SpuUpdateReqVO reqVO = randomPojo(SpuUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> spuService.updateSpu(reqVO), SPU_NOT_EXISTS); + } + + @Test + public void testDeleteSpu_success() { + // mock 数据 + ProductSpuDO dbSpu = randomPojo(ProductSpuDO.class); + ProductSpuMapper.insert(dbSpu);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSpu.getId(); + + // 调用 + spuService.deleteSpu(id); + // 校验数据不存在了 + assertNull(ProductSpuMapper.selectById(id)); + } + + @Test + public void testDeleteSpu_notExists() { + // 准备参数 + Long id = 1L; + + // 调用, 并断言异常 + assertServiceException(() -> spuService.deleteSpu(id), SPU_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetSpuPage() { + // 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))); + // 准备参数 + SpuPageReqVO reqVO = new SpuPageReqVO(); + 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.setBeginCreateTime(null); + reqVO.setEndCreateTime(null); + + // 调用 + 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.setBeginCreateTime(null); + reqVO.setEndCreateTime(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/sql/clean.sql b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql new file mode 100644 index 000000000..d9a04afed --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/clean.sql @@ -0,0 +1,3 @@ +DELETE FROM "product_category"; + +DELETE FROM "product_brand"; \ No newline at end of file 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 new file mode 100644 index 000000000..94f0b7937 --- /dev/null +++ b/yudao-module-mall/yudao-module-product-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,34 @@ +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, + "sort" int(11) NOT NULL, + "description" varchar(1024) NOT NULL, + "status" tinyint(4) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "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, + "sort" int(11), + "description" varchar(1024), + "status" tinyint(4) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "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 '品牌'; diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java new file mode 100644 index 000000000..a814edad0 --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.member.api.user.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +import java.util.Date; + +/** + * 用户信息 Response DTO + * + * @author 芋道源码 + */ +@Data +public class UserInfoDTO { + + /** + * 用户ID + */ + private Long id; + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 手机 + */ + private String mobile; + /** + * 注册 IP + */ + private String registerIp; + /** + * 最后登录IP + */ + private String loginIp; + /** + * 最后登录时间 + */ + private Date loginDate; + +} 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 8b4380ca1..2fbb61c8b 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 @@ -13,11 +13,15 @@ public interface ErrorCodeConstants { ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1004001001, "密码校验失败"); - // ========== AUTH 模块 1004003000 ========== ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用"); ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1004003004, "Token 已经过期"); ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1004003006, "获得手机号失败"); + + // ========== 用户收件地址 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/admin/user/UserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java new file mode 100644 index 000000000..795c8162c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.member.controller.admin.user; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO; +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; +import cn.iocoder.yudao.module.member.convert.user.UserConvert; +import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * @author Banging + */ +@Slf4j +@Api("用户管理") +@RestController(value = "memberUserController") +@RequestMapping("/user") +public class UserController { + + @Resource + private MemberUserService userService; + + @ApiOperation(value = "用户信息获取",notes = "用户基本信息的获取") + @GetMapping("/{tel}") + public CommonResult getUserInfo(@PathVariable String tel){ + MemberUserDO user = userService.getUserByMobile(tel); + return CommonResult.success(UserConvert.INSTANCE.convertInfo(user)); + } +} 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 new file mode 100644 index 000000000..7f943448a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http @@ -0,0 +1,54 @@ +### 请求 /create 接口 => 成功 +POST {{appApi}}//member/address/create +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6 + +{ + "userId": "245", + "name": "yunai", + "mobile": "15601691300", + "areaCode": "610632", + "detailAddress": "芋道源码 233 号 666 室", + "type": "1" +} + +### 请求 /update 接口 => 成功 +PUT {{appApi}}//member/address/update +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6 + +{ + "id": "1", + "userId": "245", + "name": "yunai888", + "mobile": "15601691300", + "areaCode": "610632", + "detailAddress": "芋道源码 233 号 666 室", + "type": "1" +} + +### 请求 /delete 接口 => 成功 +DELETE {{appApi}}//member/address/delete?id=2 +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 + +### 请求 /get 接口 => 成功 +GET {{appApi}}//member/address/get?id=1 +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 + +### 请求 /get-default 接口 => 成功 +GET {{appApi}}//member/address/get-default +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 + +### 请求 /list 接口 => 成功 +GET {{appApi}}//member/address/list +Content-Type: application/json +tenant-id: {{appTenentId}} +Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8 \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java new file mode 100644 index 000000000..ee75982fa --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.member.controller.app.address; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO; +import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO; +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.service.address.AddressService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Api(tags = "用户 APP - 用户收件地址") +@RestController +@RequestMapping("/member/address") +@Validated +public class AppAddressController { + + @Resource + private AddressService addressService; + + @PostMapping("/create") + @ApiOperation("创建用户收件地址") + public CommonResult createAddress(@Valid @RequestBody AppAddressCreateReqVO createReqVO) { + return success(addressService.createAddress(getLoginUserId(), createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新用户收件地址") + public CommonResult updateAddress(@Valid @RequestBody AppAddressUpdateReqVO updateReqVO) { + addressService.updateAddress(getLoginUserId(), updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除用户收件地址") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + public CommonResult deleteAddress(@RequestParam("id") Long id) { + addressService.deleteAddress(getLoginUserId(), id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得用户收件地址") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + public CommonResult getAddress(@RequestParam("id") Long id) { + AddressDO address = addressService.getAddress(getLoginUserId(), id); + return success(AddressConvert.INSTANCE.convert(address)); + } + + @GetMapping("/get-default") + @ApiOperation("获得默认的用户收件地址") + public CommonResult getDefaultUserAddress() { + AddressDO address = addressService.getDefaultUserAddress(getLoginUserId()); + return success(AddressConvert.INSTANCE.convert(address)); + } + + @GetMapping("/list") + @ApiOperation("获得用户收件地址列表") + public CommonResult> getAddressList() { + List list = addressService.getAddressList(getLoginUserId()); + return success(AddressConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java deleted file mode 100644 index c8c102186..000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.member.controller.app.address; 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 new file mode 100644 index 000000000..81231a881 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java @@ -0,0 +1,37 @@ +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.*; + +/** +* 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class AppAddressBaseVO { + + @ApiModelProperty(value = "收件人名称", required = true) + @NotNull(message = "收件人名称不能为空") + private String name; + + @ApiModelProperty(value = "手机号", required = true) + @NotNull(message = "手机号不能为空") + private String mobile; + + @ApiModelProperty(value = "地区编码", required = true) + @NotNull(message = "地区编码不能为空") + private Integer areaCode; + + @ApiModelProperty(value = "收件详细地址", required = true) + @NotNull(message = "收件详细地址不能为空") + private String detailAddress; + + @ApiModelProperty(value = "地址类型", required = true) + @NotNull(message = "地址类型不能为空") + @InEnum(AddressTypeEnum.class) + private Integer type; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java new file mode 100644 index 000000000..31fd89fa3 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("用户 APP - 用户收件地址创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressCreateReqVO extends AppAddressBaseVO { + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java new file mode 100644 index 000000000..1005b288c --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("用户 APP - 用户收件地址 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressRespVO extends AppAddressBaseVO { + + @ApiModelProperty(value = "编号", required = true) + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java new file mode 100644 index 000000000..b4100c76a --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.member.controller.app.address.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("用户 APP - 用户收件地址更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AppAddressUpdateReqVO extends AppAddressBaseVO { + + @ApiModelProperty(value = "编号", required = true) + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http index b1704f55d..51252530b 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http @@ -28,6 +28,17 @@ tenant-id: {{appTenentId}} "code": 9999 } +### 请求 /weixin-mini-app-login 接口 => 成功 +POST {{appApi}}/member/auth/weixin-mini-app-login +Content-Type: application/json +tenant-id: {{appTenentId}} + +{ + "phoneCode": "618e6412e0c728f5b8fc7164497463d0158a923c9e7fd86af8bba393b9decbc5", + "loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR" +} + + ### 请求 /logout 接口 => 成功 POST {{appApi}}/member/auth/logout Content-Type: application/json diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java index 6532f7aca..e42554aa8 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java @@ -104,16 +104,16 @@ public class AppAuthController { return CommonResult.success(authService.getSocialAuthorizeUrl(type, redirectUri)); } - @PostMapping("/social-quick-login") + @PostMapping("/social-login") @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户") - public CommonResult socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) { - return success(authService.socialQuickLogin(reqVO)); + public CommonResult socialLogin(@RequestBody @Valid AppAuthSocialLoginReqVO reqVO) { + return success(authService.socialLogin(reqVO)); } - @PostMapping("/social-bind-login") - @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定") - public CommonResult socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) { - return success(authService.socialBindLogin(reqVO)); + @PostMapping("/weixin-mini-app-login") + @ApiOperation("微信小程序的一键登录") + public CommonResult weixinMiniAppLogin(@RequestBody @Valid AppAuthWeixinMiniAppLoginReqVO reqVO) { + return success(authService.weixinMiniAppLogin(reqVO)); } } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java index 1055e979b..d8fe3940f 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.member.controller.app.auth.vo; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -9,9 +12,10 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotEmpty; -@ApiModel("用户 APP - 手机 + 密码登录 Request VO") +@ApiModel(value = "用户 APP - 手机 + 密码登录 Request VO", description = "如果登录并绑定社交用户,需要传递 social 开头的参数") @Data @NoArgsConstructor @AllArgsConstructor @@ -28,4 +32,26 @@ public class AppAuthLoginReqVO { @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @ApiModelProperty(value = "授权码", required = true, example = "1024") + private String socialCode; + + @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java index 063caf80d..dd6de5d59 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSmsLoginReqVO.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.member.controller.app.auth.vo; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -9,10 +12,11 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -@ApiModel("用户 APP - 手机 + 验证码登录 Request VO") +@ApiModel(value = "用户 APP - 手机 + 验证码登录 Request VO", description = "如果登录并绑定社交用户,需要传递 social 开头的参数") @Data @NoArgsConstructor @AllArgsConstructor @@ -30,4 +34,26 @@ public class AppAuthSmsLoginReqVO { @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") private String code; + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @ApiModelProperty(value = "授权码", required = true, example = "1024") + private String socialCode; + + @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindLoginReqVO.java deleted file mode 100644 index e5c173768..000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialBindLoginReqVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.member.controller.app.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -@ApiModel("用户 APP - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AppAuthSocialBindLoginReqVO { - - @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @ApiModelProperty(value = "授权码", required = true, example = "1024") - @NotEmpty(message = "授权码不能为空") - private String code; - - @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - @NotEmpty(message = "state 不能为空") - private String state; - - @ApiModelProperty(value = "手机号", required = true, example = "15119100000") - @NotEmpty(message = "手机号不能为空") - @Length(min = 11, max = 11, message = "手机号是11位数字") - private String mobile; - - @ApiModelProperty(value = "手机验证码", required = true, example = "1024") - @NotEmpty(message = "手机验证码不能为空") - @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") - @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") - private String smsCode; - -} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialQuickLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java similarity index 96% rename from yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialQuickLoginReqVO.java rename to yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java index 02c26bcb3..7a5856d67 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialQuickLoginReqVO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthSocialLoginReqVO.java @@ -17,7 +17,7 @@ import javax.validation.constraints.NotNull; @NoArgsConstructor @AllArgsConstructor @Builder -public class AppAuthSocialQuickLoginReqVO { +public class AppAuthSocialLoginReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") @InEnum(SocialTypeEnum.class) diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java new file mode 100644 index 000000000..bd34085b2 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.member.controller.app.auth.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("用户 APP - 微信小程序手机登录 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AppAuthWeixinMiniAppLoginReqVO { + + @ApiModelProperty(value = "手机 code", required = true, example = "hello", notes = "小程序通过 wx.getPhoneNumber 方法获得") + @NotEmpty(message = "手机 code 不能为空") + private String phoneCode; + + @ApiModelProperty(value = "登录 code", required = true, example = "word", notes = "小程序通过 wx.login 方法获得") + @NotEmpty(message = "登录 code 不能为空") + private String loginCode; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java index edefcdba0..4eec2893f 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppUserController.java @@ -40,7 +40,7 @@ public class AppUserController { return success(true); } - @PutMapping("/update-avatar") + @PostMapping("/update-avatar") @ApiOperation("修改用户头像") @PreAuthenticated public CommonResult updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws Exception { diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java new file mode 100644 index 000000000..fdc01e5a8 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.member.convert.address; + +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.member.controller.app.address.vo.*; +import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO; + +/** + * 用户收件地址 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface AddressConvert { + + AddressConvert INSTANCE = Mappers.getMapper(AddressConvert.class); + + AddressDO convert(AppAddressCreateReqVO bean); + + AddressDO convert(AppAddressUpdateReqVO bean); + + AppAddressRespVO convert(AddressDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java index 7c73ab829..755d71a21 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java @@ -16,8 +16,7 @@ public interface AuthConvert { AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); - SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); - SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); + SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialLoginReqVO reqVO); SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); SmsCodeSendReqDTO convert(AppAuthSmsSendReqVO reqVO); diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java index b72051d63..6be0d6b41 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.member.convert.user; +import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO; import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; @@ -14,4 +15,5 @@ public interface UserConvert { AppUserInfoRespVO convert(MemberUserDO bean); UserRespDTO convert2(MemberUserDO bean); + UserInfoDTO convertInfo(MemberUserDO bean); } 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 new file mode 100644 index 000000000..546df00c6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java @@ -0,0 +1,55 @@ +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.*; + +/** + * 用户收件地址 DO + * + * @author 芋道源码 + */ +@TableName("member_address") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AddressDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 收件人名称 + */ + private String name; + /** + * 手机号 + */ + private String mobile; + /** + * 地区编码 + */ + private Integer areaCode; + /** + * 收件详细地址 + */ + private String detailAddress; + /** + * 地址类型 + * + * 枚举 {@link AddressTypeEnum} + */ + private Integer type; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/package-info.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/package-info.java deleted file mode 100644 index c318d2c94..000000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.member.dal.dataobject.address; 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 new file mode 100644 index 000000000..fc02e5879 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java @@ -0,0 +1,64 @@ +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.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); + } + + /** + * 获取地址列表 + * @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())); + } + +} 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 new file mode 100644 index 000000000..59465e495 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java @@ -0,0 +1,30 @@ +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/service/address/AddressService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java new file mode 100644 index 000000000..e45b075fe --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java @@ -0,0 +1,59 @@ +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.dal.dataobject.address.AddressDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +/** + * 用户收件地址 Service 接口 + * + * @author 芋道源码 + */ +public interface AddressService { + + /** + * 创建用户收件地址 + * + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAddress(Long userId, @Valid AppAddressCreateReqVO createReqVO); + + /** + * 更新用户收件地址 + * + * @param userId 用户编号 + * @param updateReqVO 更新信息 + */ + void updateAddress(Long userId, @Valid AppAddressUpdateReqVO updateReqVO); + + /** + * 删除用户收件地址 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deleteAddress(Long userId, Long id); + + /** + * 获得用户收件地址 + * + * @param id 编号 + * @return 用户收件地址 + */ + AddressDO getAddress(Long userId, Long id); + + /** + * 获得用户收件地址列表 + * + * @param userId 用户编号 + * @return 用户收件地址列表 + */ + List getAddressList(Long userId); + + 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 new file mode 100644 index 000000000..a9cd91d59 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java @@ -0,0 +1,133 @@ +package cn.iocoder.yudao.module.member.service.address; + +import cn.iocoder.yudao.module.member.enums.AddressTypeEnum; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; + +/** + * 用户收件地址 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AddressServiceImpl implements AddressService { + + @Resource + private AddressMapper addressMapper; + + @Override + @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)); + } + // 插入 + AddressDO address = AddressConvert.INSTANCE.convert(createReqVO); + address.setUserId(userId); + addressMapper.insert(address); + // 返回 + return address.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) { + // 校验存在,校验是否能够操作 + check(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)); + } + // 更新 + AddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO); + addressMapper.updateById(updateObj); + } + + @Override + public void deleteAddress(Long userId, Long id) { + // 校验存在,校验是否能够操作 + check(userId, id); + // 删除 + addressMapper.deleteById(id); + } + + /** + * 校验用户收件地址是不是属于该用户 + * + * @param userId 用户编号 + * @param userAddressId 用户收件地址 + */ + private void check(Long userId, Long userAddressId) { + AddressDO addressDO = getAddress(userId, userAddressId); + if(null == addressDO){ + 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); + } + + @Override + public List getAddressList(Long userId) { + return selectListByUserIdAndType(userId, null); + } + + /** + * 获取默认地址 + * @param userId + * @return + */ + @Override + public AddressDO getDefaultUserAddress(Long userId) { + return addressMapper.getDefaultUserAddress(userId); + } + + /** + * 根据类型获取地址列表 + * @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/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java index ded192217..6990fba2e 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java @@ -42,15 +42,15 @@ public interface MemberAuthService { * @param reqVO 登录信息 * @return 登录结果 */ - AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO); + AppAuthLoginRespVO socialLogin(@Valid AppAuthSocialLoginReqVO reqVO); /** - * 社交登录,使用 手机号 + 手机验证码 + * 微信小程序的一键登录 * * @param reqVO 登录信息 * @return 登录结果 */ - AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO); + AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO); /** * 获得社交认证 URL @@ -89,4 +89,5 @@ public interface MemberAuthService { * @return 登录结果 */ AppAuthLoginRespVO refreshToken(String refreshToken); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java index 0d92c14e1..d8c59ad37 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.member.service.auth; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -11,17 +13,19 @@ import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi; import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO; import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.social.SocialUserApi; -import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 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.enums.social.SocialTypeEnum; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.password.PasswordEncoder; @@ -55,6 +59,9 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Resource private OAuth2TokenApi oauth2TokenApi; + @Resource + private WxMaService wxMaService; + @Resource private PasswordEncoder passwordEncoder; @Resource @@ -65,6 +72,12 @@ public class MemberAuthServiceImpl implements MemberAuthService { // 使用手机 + 密码,进行登录。 MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); + // 如果 socialType 非空,说明需要绑定社交用户 + if (reqVO.getSocialType() != null) { + socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); } @@ -80,12 +93,18 @@ public class MemberAuthServiceImpl implements MemberAuthService { MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); Assert.notNull(user, "获取用户失败,结果为空"); + // 如果 socialType 非空,说明需要绑定社交用户 + if (reqVO.getSocialType() != null) { + socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS); } @Override - public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) { + public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) { // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), reqVO.getCode(), reqVO.getState()); @@ -104,15 +123,24 @@ public class MemberAuthServiceImpl implements MemberAuthService { } @Override - public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) { - // 使用手机号、手机验证码登录 - AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() - .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); - AppAuthLoginRespVO token = smsLogin(loginReqVO); + public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) { + // 获得对应的手机号信息 + WxMaPhoneNumberInfo phoneNumberInfo; + try { + phoneNumberInfo = wxMaService.getUserService().getNewPhoneNoInfo(reqVO.getPhoneCode()); + } catch (Exception exception) { + throw exception(AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR); + } + // 获得获得注册用户 + MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(), getClientIP()); + Assert.notNull(user, "获取用户失败,结果为空"); // 绑定社交用户 - socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO)); - return token; + socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), "")); + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL); } private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) { @@ -120,7 +148,8 @@ public class MemberAuthServiceImpl implements MemberAuthService { createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS); // 创建 Token 令牌 OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO() - .setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)); + .setUserId(user.getId()).setUserType(getUserType().getValue()) + .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)); // 构建返回结果 return AuthConvert.INSTANCE.convert(accessTokenRespDTO); } 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 new file mode 100644 index 000000000..eefa2e13e --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.member.service.address; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +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.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.*; + +/** +* {@link AddressServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(AddressServiceImpl.class) +public class AddressServiceImplTest extends BaseDbUnitTest { + + @Resource + private AddressServiceImpl addressService; + + @Resource + private AddressMapper addressMapper; + + @Test + public void testCreateAddress_success() { + // 准备参数 + AppAddressCreateReqVO reqVO = randomPojo(AppAddressCreateReqVO.class); + + // 调用 + Long addressId = addressService.createAddress(getLoginUserId(), reqVO); + // 断言 + assertNotNull(addressId); + // 校验记录的属性是否正确 + AddressDO address = addressMapper.selectById(addressId); + assertPojoEquals(reqVO, address); + } + + @Test + public void testUpdateAddress_success() { + // mock 数据 + AddressDO dbAddress = randomPojo(AddressDO.class); + addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据 + // 准备参数 + AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class, o -> { + o.setId(dbAddress.getId()); // 设置更新的 ID + }); + + // 调用 + addressService.updateAddress(getLoginUserId(), reqVO); + // 校验是否更新正确 + AddressDO address = addressMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, address); + } + + @Test + public void testUpdateAddress_notExists() { + // 准备参数 + AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> addressService.updateAddress(getLoginUserId(), reqVO), ADDRESS_NOT_EXISTS); + } + + @Test + public void testDeleteAddress_success() { + // mock 数据 + AddressDO dbAddress = randomPojo(AddressDO.class); + addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbAddress.getId(); + + // 调用 + addressService.deleteAddress(getLoginUserId(), id); + // 校验数据不存在了 + assertNull(addressMapper.selectById(id)); + } + + @Test + public void testDeleteAddress_notExists() { + // 准备参数 + 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))); + } + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql index 92f559dc1..bb8eddf50 100644 --- a/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-member/yudao-module-member-biz/src/test/resources/sql/clean.sql @@ -1 +1,2 @@ DELETE FROM "member_user"; +DELETE FROM "member_address"; 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 85925f30b..608a3bddf 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 @@ -30,3 +30,20 @@ CREATE TABLE IF NOT EXISTS "inf_file" ( PRIMARY KEY ("id") ) COMMENT '文件表'; +CREATE TABLE IF NOT EXISTS "member_address" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint(20) NOT NULL, + "name" varchar(10) NOT NULL, + "mobile" varchar(20) NOT NULL, + "area_code" int(11) NOT NULL, + "detail_address" varchar(250) NOT NULL, + "type" tinyint(4) 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 '用户收件地址'; + diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java index ccc1c62ae..c591df2c3 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.system.api.social.dto; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -14,6 +16,8 @@ import javax.validation.constraints.NotNull; * @author 芋道源码 */ @Data +@NoArgsConstructor +@AllArgsConstructor public class SocialUserBindReqDTO { /** @@ -42,7 +46,7 @@ public class SocialUserBindReqDTO { /** * state */ - @NotEmpty(message = "state 不能为空") + @NotNull(message = "state 不能为空") private String state; } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java index 77833b2e6..197bb2943 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java @@ -6,9 +6,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Arrays; -import java.util.Collection; -import java.util.Set; -import java.util.stream.Collectors; /** * 社交平台的类型枚举 @@ -49,7 +46,7 @@ public enum SocialTypeEnum implements IntArrayValuable { * 微信小程序 * 文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html */ - WECHAT_MINI_PROGRAM(33, "WECHAT_MINI_PROGRAM"), + WECHAT_MINI_APP(34, "WECHAT_MINI_APP"), ; public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray(); 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 e42a5dfa5..ce44d2153 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 @@ -141,23 +141,16 @@ public class AuthController { @ApiImplicitParam(name = "type", value = "社交类型", required = true, dataTypeClass = Integer.class), @ApiImplicitParam(name = "redirectUri", value = "回调路径", dataTypeClass = String.class) }) - public CommonResult socialAuthRedirect(@RequestParam("type") Integer type, + public CommonResult socialLogin(@RequestParam("type") Integer type, @RequestParam("redirectUri") String redirectUri) { return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri)); } - @PostMapping("/social-quick-login") - @ApiOperation("社交快捷登录,使用 code 授权码") + @PostMapping("/social-login") + @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户") @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 - public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) { - return success(authService.socialQuickLogin(reqVO)); - } - - @PostMapping("/social-bind-login") - @ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 - public CommonResult socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) { - return success(authService.socialBindLogin(reqVO)); + public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { + return success(authService.socialLogin(reqVO)); } } 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 0ecd3d670..67e80d24a 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 @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.system.controller.admin.auth.vo; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -8,10 +11,11 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -@ApiModel("管理后台 - 账号密码登录 Request VO") +@ApiModel(value = "管理后台 - 账号密码登录 Request VO", description = "如果登录并绑定社交用户,需要传递 social 开头的参数") @Data @NoArgsConstructor @AllArgsConstructor @@ -29,6 +33,8 @@ public class AuthLoginReqVO { @Length(min = 4, max = 16, message = "密码长度为 4-16 位") private String password; + // ========== 图片验证码相关 ========== + @ApiModelProperty(value = "验证码", required = true, example = "1024", notes = "验证码开启时,需要传递") @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) private String code; @@ -37,9 +43,31 @@ public class AuthLoginReqVO { @NotEmpty(message = "唯一标识不能为空", groups = CodeEnableGroup.class) private String uuid; + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @ApiModelProperty(value = "授权码", required = true, example = "1024") + private String socialCode; + + @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + /** * 开启验证码的 Group */ public interface CodeEnableGroup {} + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java deleted file mode 100644 index cefe40d14..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -@ApiModel("管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthSocialBindLoginReqVO { - - @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @ApiModelProperty(value = "授权码", required = true, example = "1024") - @NotEmpty(message = "授权码不能为空") - private String code; - - @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - @NotEmpty(message = "state 不能为空") - private String state; - - @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma") - @NotEmpty(message = "登录账号不能为空") - @Length(min = 4, max = 16, message = "账号长度为 4-16 位") - @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") - private String username; - - @ApiModelProperty(value = "密码", required = true, example = "buzhidao") - @NotEmpty(message = "密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java similarity index 89% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java index 4b7ebb175..e52a49288 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.auth.vo; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -12,12 +12,12 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -@ApiModel("管理后台 - 社交快捷登录 Request VO,使用 code 授权码") +@ApiModel("管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") @Data @NoArgsConstructor @AllArgsConstructor @Builder -public class AuthSocialQuickLoginReqVO { +public class AuthSocialLoginReqVO { @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") @InEnum(SocialTypeEnum.class) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index 62784defa..38e84ea1d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -65,8 +65,7 @@ public interface AuthConvert { return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId())); } - SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialBindLoginReqVO reqVO); - SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialQuickLoginReqVO reqVO); + SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO); SmsCodeSendReqDTO convert(AuthSmsSendReqVO reqVO); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index c81809b2e..5b1114fb7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -24,8 +24,7 @@ public class SecurityConfiguration { registry.antMatchers(buildAdminApi("/system/auth/refresh-token")).permitAll(); // 社交登陆的接口 registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll(); - registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll(); - registry.antMatchers(buildAdminApi("/system/auth/social-bind-login")).permitAll(); + registry.antMatchers(buildAdminApi("/system/auth/social-login")).permitAll(); // 登录登录的接口 registry.antMatchers(buildAdminApi("/system/auth/sms-login")).permitAll(); registry.antMatchers(buildAdminApi("/system/auth/send-sms-code")).permitAll(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java index 3a53c1aa3..52796ec2f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -60,15 +60,7 @@ public interface AdminAuthService { * @param reqVO 登录信息 * @return 登录结果 */ - AuthLoginRespVO socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO); - - /** - * 社交绑定登录,使用 code 授权码 + 账号密码 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO); + AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); /** * 刷新访问令牌 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 517e058f1..99e0ff1e6 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 @@ -8,13 +8,14 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; 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.oauth2.OAuth2ClientConstants; 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; @@ -91,6 +92,12 @@ public class AdminAuthServiceImpl implements AdminAuthService { // 使用账号密码,进行登录 AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); + // 如果 socialType 非空,说明需要绑定社交用户 + if (reqVO.getSocialType() != null) { + socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); } @@ -166,7 +173,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { } @Override - public AuthLoginRespVO socialQuickLogin(AuthSocialQuickLoginReqVO reqVO) { + public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getCode(), reqVO.getState()); @@ -184,18 +191,6 @@ public class AdminAuthServiceImpl implements AdminAuthService { return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); } - @Override - public AuthLoginRespVO socialBindLogin(AuthSocialBindLoginReqVO reqVO) { - // 使用账号密码,进行登录。 - AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); - - // 绑定社交用户 - socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO)); - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); - } - @Override public AuthLoginRespVO refreshToken(String refreshToken) { OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java index 723e507c4..411d749b0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java @@ -3,13 +3,13 @@ package cn.iocoder.yudao.module.system.service.social; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import com.xkcoding.justauth.AuthRequestFactory; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthResponse; @@ -39,8 +39,8 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @Slf4j public class SocialUserServiceImpl implements SocialUserService { - @Resource - private AuthRequestFactory authRequestFactory; + @Resource// 由于自定义了 YudaoAuthRequestFactory 无法覆盖默认的 AuthRequestFactory,所以只能注入它 + private YudaoAuthRequestFactory yudaoAuthRequestFactory; @Resource private SocialUserBindMapper socialUserBindMapper; @@ -50,7 +50,7 @@ public class SocialUserServiceImpl implements SocialUserService { @Override public String getAuthorizeUrl(Integer type, String redirectUri) { // 获得对应的 AuthRequest 实现 - AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); + AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); // 生成跳转地址 String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); @@ -153,7 +153,7 @@ public class SocialUserServiceImpl implements SocialUserService { * @return 授权的用户 */ private AuthUser getAuthUser(Integer type, String code, String state) { - AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); + AuthRequest authRequest = yudaoAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build(); AuthResponse authResponse = authRequest.login(authCallback); log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", type, diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index d3faf0e1e..8dec9b8d3 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -41,16 +41,26 @@ yudao-module-pay-biz ${revision} + + cn.iocoder.boot + yudao-module-market-biz + ${revision} + + + cn.iocoder.boot + yudao-module-product-biz + ${revision} + cn.iocoder.boot yudao-module-bpm-biz-flowable ${revision} - - - - + + + + cn.iocoder.boot diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 87d943f92..177abf9e4 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -173,10 +173,9 @@ logging: cn.iocoder.yudao.module.tool.dal.mysql: debug cn.iocoder.yudao.module.member.dal.mysql: debug ---- #################### 微信公众号相关配置 #################### -wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 - mp: - # 公众号配置(必填) +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 app-id: wx041349c6f39b268b secret: 5abee519483bc9f8cb37ce280e814bd0 # 存储配置,解决 AccessToken 的跨节点的共享 @@ -184,6 +183,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 请求微信公众号平台 --- #################### 芋道相关配置 #################### @@ -220,6 +226,12 @@ justauth: client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw agent-id: 1000004 ignore-check-redirect-uri: true + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + cache: type: REDIS prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: diff --git a/yudao-ui-admin/src/api/login.js b/yudao-ui-admin/src/api/login.js index a76decf17..390f66900 100644 --- a/yudao-ui-admin/src/api/login.js +++ b/yudao-ui-admin/src/api/login.js @@ -3,12 +3,15 @@ import {getRefreshToken} from "@/utils/auth"; import service from "@/utils/request"; // 登录方法 -export function login(username, password, code, uuid) { +export function login(username, password, code, uuid, + socialType, socialCode, socialState) { const data = { username, password, code, - uuid + uuid, + // 社交相关 + socialType, socialCode, socialState } return request({ url: '/system/auth/login', @@ -51,9 +54,9 @@ export function socialAuthRedirect(type, redirectUri) { } // 社交快捷登录,使用 code 授权码 -export function socialQuickLogin(type, code, state) { +export function socialLogin(type, code, state) { return request({ - url: '/system/auth/social-quick-login', + url: '/system/auth/social-login', method: 'post', data: { type, @@ -63,21 +66,6 @@ export function socialQuickLogin(type, code, state) { }) } -// 社交绑定登录,使用 code 授权码 + + 账号密码 -export function socialBindLogin(type, code, state, username, password) { - return request({ - url: '/system/auth/social-bind-login', - method: 'post', - data: { - type, - code, - state, - username, - password - } - }) -} - // 获取登录验证码 export function sendSmsCode(mobile, scene) { return request({ diff --git a/yudao-ui-admin/src/api/mall/market/banner.js b/yudao-ui-admin/src/api/mall/market/banner.js new file mode 100644 index 000000000..57c55def9 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/market/banner.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +// 创建Banner +export function createBanner(data) { + return request({ + url: '/market/banner/create', + method: 'post', + data: data + }) +} + +// 更新Banner +export function updateBanner(data) { + return request({ + url: '/market/banner/update', + method: 'put', + data: data + }) +} + +// 删除Banner +export function deleteBanner(id) { + return request({ + url: '/market/banner/delete?id=' + id, + method: 'delete' + }) +} + +// 获得Banner +export function getBanner(id) { + return request({ + url: '/market/banner/get?id=' + id, + method: 'get' + }) +} + +// 获得Banner分页 +export function getBannerPage(query) { + return request({ + url: '/market/banner/page', + method: 'get', + params: query + }) +} + +// 导出Banner Excel +export function exportBannerExcel(query) { + return request({ + url: '/market/banner/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/brand.js b/yudao-ui-admin/src/api/mall/product/brand.js new file mode 100644 index 000000000..69cb61c08 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/product/brand.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +// 创建品牌 +export function createBrand(data) { + return request({ + url: '/product/brand/create', + method: 'post', + data: data + }) +} + +// 更新品牌 +export function updateBrand(data) { + return request({ + url: '/product/brand/update', + method: 'put', + data: data + }) +} + +// 删除品牌 +export function deleteBrand(id) { + return request({ + url: '/product/brand/delete?id=' + id, + method: 'delete' + }) +} + +// 获得品牌 +export function getBrand(id) { + return request({ + url: '/product/brand/get?id=' + id, + method: 'get' + }) +} + +// 获得品牌分页 +export function getBrandPage(query) { + return request({ + url: '/product/brand/page', + method: 'get', + params: query + }) +} + +// 导出品牌 Excel +export function exportBrandExcel(query) { + return request({ + url: '/product/brand/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/category.js b/yudao-ui-admin/src/api/mall/product/category.js new file mode 100644 index 000000000..bd825034d --- /dev/null +++ b/yudao-ui-admin/src/api/mall/product/category.js @@ -0,0 +1,63 @@ +import request from '@/utils/request' + +// 创建商品分类 +export function createCategory(data) { + return request({ + url: '/product/category/create', + method: 'post', + data: data + }) +} + +// 更新商品分类 +export function updateCategory(data) { + return request({ + url: '/product/category/update', + method: 'put', + data: data + }) +} + +// 删除商品分类 +export function deleteCategory(id) { + return request({ + url: '/product/category/delete?id=' + id, + method: 'delete' + }) +} + +// 获得商品分类 +export function getCategory(id) { + return request({ + url: '/product/category/get?id=' + id, + method: 'get' + }) +} + +// 获得商品分类 +export function listCategory(query) { + return request({ + url: '/product/category/listByQuery', + method: 'get', + params: query + }) +} + +// 获得商品分类分页 +export function getCategoryPage(query) { + return request({ + url: '/product/category/page', + method: 'get', + params: query + }) +} + +// 导出商品分类 Excel +export function exportCategoryExcel(query) { + return request({ + url: '/product/category/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/property.js b/yudao-ui-admin/src/api/mall/product/property.js new file mode 100644 index 000000000..58ae320f6 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/product/property.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +// 创建规格名称 +export function createProperty(data) { + return request({ + url: '/product/property/create', + method: 'post', + data: data + }) +} + +// 更新规格名称 +export function updateProperty(data) { + return request({ + url: '/product/property/update', + method: 'put', + data: data + }) +} + +// 删除规格名称 +export function deleteProperty(id) { + return request({ + url: '/product/property/delete?id=' + id, + method: 'delete' + }) +} + +// 获得规格名称 +export function getProperty(id) { + return request({ + url: '/product/property/get?id=' + id, + method: 'get' + }) +} + +// 获得规格名称分页 +export function getPropertyPage(query) { + return request({ + url: '/product/property/page', + method: 'get', + params: query + }) +} + +// 导出规格名称 Excel +export function exportPropertyExcel(query) { + return request({ + url: '/product/property/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/api/mall/product/spu.js b/yudao-ui-admin/src/api/mall/product/spu.js new file mode 100644 index 000000000..068e7d838 --- /dev/null +++ b/yudao-ui-admin/src/api/mall/product/spu.js @@ -0,0 +1,54 @@ +import request from '@/utils/request' + +// 创建商品spu +export function createSpu(data) { + return request({ + url: '/product/spu/create', + method: 'post', + data: data + }) +} + +// 更新商品spu +export function updateSpu(data) { + return request({ + url: '/product/spu/update', + method: 'put', + data: data + }) +} + +// 删除商品spu +export function deleteSpu(id) { + return request({ + url: '/product/spu/delete?id=' + id, + method: 'delete' + }) +} + +// 获得商品spu +export function getSpu(id) { + return request({ + url: '/product/spu/get?id=' + id, + method: 'get' + }) +} + +// 获得商品spu分页 +export function getSpuPage(query) { + return request({ + url: '/product/spu/page', + method: 'get', + params: query + }) +} + +// 导出商品spu Excel +export function exportSpuExcel(query) { + return request({ + url: '/product/spu/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/components/ImageUpload/index.vue b/yudao-ui-admin/src/components/ImageUpload/index.vue index 1e95d5da7..3f2bda7cc 100644 --- a/yudao-ui-admin/src/components/ImageUpload/index.vue +++ b/yudao-ui-admin/src/components/ImageUpload/index.vue @@ -1,21 +1,21 @@