diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d416ccce --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +### gradle ### +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +### STS ### +.settings/ +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +*.lock +rebel.xml + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### maven ### +target/ +*.war +*.ear +*.zip +*.tar +*.tar.gz + +### logs #### +/logs/ +*.log + +### temp ignore ### +*.cache +*.diff +*.patch +*.tmp +*.java~ +*.properties~ +*.xml~ + +### system ignore ### +.DS_Store +Thumbs.db +Servers +.metadata +upload +gen_code + +### node ### +node_modules diff --git a/README.md b/README.md new file mode 100644 index 00000000..e6be9b11 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# README + +#### ![image-20210705143529597](doc/img/readme/image-20210705143529597.png) + +### 前言 + +本商城是基于Spring Cloud、Nacos、Seata、Mysql、Redis、RocketMQ、canal、ElasticSearch、minio的微服务B2B2C电商商城系统,采用主流的互联网技术架构、全新的UI设计、支持集群部署、服务注册和发现以及拥有完整的订单流程等,代码完全开源,没有任何二次封装,是一个非常适合二次开发的电商平台系统。 + +本商城致力于为中大型企业打造一个功能完整、易于维护的微服务B2B2C电商商城系统,采用主流微服务技术实现。后台管理系统包含平台管理,店铺管理、商品管理、订单管理、规格管理、权限管理、资源管理等模块。 + +### 文档 + +这代码有没有文档呀? 当然有啦,你已经下载了,在doc这个文件夹上,实在不知道,我就给链接出来咯: + +gitee:https://gitee.com/gz-yami/mall4cloud/tree/master/doc + +### 授权 + +除开源版本外,本商城还提供商业版本的B2B2C商城,采用PC+小程序+APP,欲知详情,请访问官网。 + +商城官网:https://www.mall4j.com + +商城使用 AGPLv3 开源,请遵守 AGPLv3 的相关条款,或者联系作者获取商业授权(https://www.mall4j.com) + +### 项目链接 + +JAVA后台:https://gitee.com/gz-yami/mall4cloud + +平台端:https://gitee.com/gz-yami/mall4cloud-platform + +商家端:https://gitee.com/gz-yami/mall4cloud-multishop + +uni-app:https://gitee.com/gz-yami/mall4cloud-uniapp + +### 演示地址 + +商业版演示地址: + +pc端:https://cloud-pc.mall4j.com + +H5端:https://h5.mall4j.com/cloud + +小程序:微信搜索 “mall4j微服务版” + + + +### 技术选型 + +| 技术 | 版本 | 说明 | +| :-----------: | :-----: | :-----------: | +| Spring Boot | 2.4 | MVC核心框架 | +| Feign | 3.0 | 服务调用 | +| Nacos | 2.0 | 服务注册与发现 | +| Seata | 1.4 | 分布式事务 | +| Mybatis | 2.1 | 持久层框架 | +| hutool | 5.5 | JAVA工具集 | +| pagehelper | 1.3 | Mybatis分页插件 | +| Redis | 2.4 | 分布式缓存 | +| RocketMQ | 2.2 | 消息队列 | +| canal | 1.1 | 数据库同步 | +| Spring Cloud Gateway | 3.0 | 网关 | +| Spring Cloud LoadBalancer | 3.0 | 负载均衡 | +| ElasticSearch | 7.9 | 数据搜索 | +| minio | 8.0 | 文件上传 | +| Knife4j | 3.0 | MVC框架集成Swagger生成Api文档 | +| Element UI | 2.13 | UI框架 | +| vue、uni-app | vue2.6 | JS框架 | + +### 部署教程 + +部署教程请参考该文件夹下的`/基本开发文档/mall4cloud开发环境搭建.md`以及`/开发环境搭建`目录下的中间件安装。 + +### 开源版相关截图 + +#### 1.后台截图 + +- 平台端 + + ![image-20210705152010036](doc/img/readme/image-20210705152010036.png) + + ![image-20210705152109738](doc/img/readme/image-20210705152109738.png) + +- 商家端 + + ![image-20210705151729559](doc/img/readme/image-20210705151729559.png) + + ![image-20210705151847270](doc/img/readme/image-20210705151847270.png) + +#### 2.小程序截图 + +![小程序-1625472143277](doc/img/readme/小程序-1625472143277.png) + +#### 3.uni-app截图 + +![uniapp-1625469707350](doc/img/readme/uniapp-1625469707350.png) + +### + +## 提交反馈 +- Mall4j官网 https://www.mall4j.com +- 商务合作微信 + +![输入图片说明](https://images.gitee.com/uploads/images/2021/0703/131508_13858876_5094767.jpeg "法宝微信2.jpg") + + +- mall4j开源技术QQ群:722835385 + +![输入图片说明](https://images.gitee.com/uploads/images/2021/0703/110919_835cf484_5094767.jpeg "mall4j群.jpg") + +## 你的点赞鼓励,是我们前进的动力~ +## 你的点赞鼓励,是我们前进的动力~ +## 你的点赞鼓励,是我们前进的动力~ \ No newline at end of file diff --git a/db/mall4cloud.sql b/db/mall4cloud.sql new file mode 100644 index 00000000..8c09cec4 --- /dev/null +++ b/db/mall4cloud.sql @@ -0,0 +1,1471 @@ +create database IF NOT EXISTS `mall4cloud_auth` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_auth; + +-- ---------------------------- +-- Table structure for auth_account +-- ---------------------------- +DROP TABLE IF EXISTS `auth_account`; +CREATE TABLE `auth_account` ( + `uid` bigint UNSIGNED NOT NULL COMMENT '全平台用户唯一id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '用户名', + `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '密码', + `create_ip` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '创建ip', + `status` tinyint NOT NULL COMMENT '状态 1:启用 0:禁用 -1:删除', + `sys_type` tinyint NOT NULL COMMENT '用户类型见SysTypeEnum 0.普通用户系统 1.商家端 2平台端', + `user_id` bigint NOT NULL COMMENT '用户id', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '所属租户', + `is_admin` tinyint NULL DEFAULT NULL COMMENT '是否是管理员', + PRIMARY KEY (`uid`) USING BTREE, + UNIQUE INDEX `uk_usertype_userid`(`sys_type`, `user_id`) USING BTREE, + INDEX `idx_username`(`username`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_is_0900_ai_ci COMMENT = '统一账户信息'; + +-- ---------------------------- +-- Records of auth_account +-- ---------------------------- +INSERT INTO `auth_account` VALUES (1, '2021-07-01 11:07:38', '2021-07-03 13:11:52', 'admin', '$2a$10$EiwfzqsAVUtuJ0Ry5YPMPOeyc/4shzSUcqMBRInKTijzir48LLkM.', '127.0.0.1', 1, 2, 1, 0, 1); + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 385 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_biz` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_biz; + +-- ---------------------------- +-- Table structure for attach_file +-- ---------------------------- +DROP TABLE IF EXISTS `attach_file`; +CREATE TABLE `attach_file` ( + `file_id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `file_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件路径', + `file_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件类型', + `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件名', + `file_size` int NULL DEFAULT NULL COMMENT '文件大小', + `shop_id` bigint NULL DEFAULT NULL COMMENT '店铺id', + `type` tinyint NULL DEFAULT NULL COMMENT '文件 1:图片 2:视频 3:文件', + `attach_file_group_id` bigint NULL DEFAULT 0 COMMENT '文件分组id', + PRIMARY KEY (`file_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1431 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '上传文件记录表'; + +-- ---------------------------- +-- Table structure for attach_file_group +-- ---------------------------- +DROP TABLE IF EXISTS `attach_file_group`; +CREATE TABLE `attach_file_group` ( + `attach_file_group_id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NOT NULL COMMENT '店铺id', + `name` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分组名称', + `type` tinyint NOT NULL COMMENT '1:图片 2:视频 3:文件', + PRIMARY KEY (`attach_file_group_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 162 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_leaf` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_leaf; + +-- ---------------------------- +-- Table structure for leaf_alloc +-- ---------------------------- +DROP TABLE IF EXISTS `leaf_alloc`; +CREATE TABLE `leaf_alloc` ( + `biz_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '区分业务', + `max_id` bigint UNSIGNED NOT NULL DEFAULT 1 COMMENT '该biz_tag目前所被分配的ID号段的最大值', + `step` int NOT NULL COMMENT '每次分配的号段长度', + `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `description` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述', + `random_step` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '每次getid时随机增加的长度,这样就不会有连续的id了', + PRIMARY KEY (`biz_tag`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of leaf_alloc +-- ---------------------------- +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-auth-account', 112000, 100, '2021-07-01 14:16:40', 'mall4cloud-multishop数据库中auth_account这张表的uid', 10); +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-multishop-user', 110400, 100, '2021-07-01 11:10:32', 'mall4cloud-multishop数据库中shop_user这张表的id', 10); +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-order', 1568693084, 100, '2021-07-05 15:25:19', '订单id', 10); +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-pay', 564994845, 100, '2021-07-05 15:14:40', '支付单号', 10); +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-platform-user', 101600, 100, '2021-07-03 13:13:54', 'mall4cloud-platform数据库中sys_user这张表的id', 10); +INSERT INTO `leaf_alloc` VALUES ('mall4cloud-user', 106600, 100, '2021-07-01 11:22:26', 'mall4cloud-user数据库中user这张表的id', 10); + + +create database IF NOT EXISTS `mall4cloud_multishop` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_multishop; + +-- ---------------------------- +-- Table structure for hot_search +-- ---------------------------- +DROP TABLE IF EXISTS `hot_search`; +CREATE TABLE `hot_search` ( + `hot_search_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NULL DEFAULT NULL COMMENT '店铺ID 0为全平台热搜', + `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容', + `seq` int NULL DEFAULT NULL COMMENT '顺序', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态 0下线 1上线', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '热搜标题', + PRIMARY KEY (`hot_search_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 88 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '热搜'; + +-- ---------------------------- +-- Table structure for index_img +-- ---------------------------- +DROP TABLE IF EXISTS `index_img`; +CREATE TABLE `index_img` ( + `img_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NOT NULL COMMENT '店铺ID', + `img_url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '图片', + `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态 1:enable, 0:disable', + `seq` int NOT NULL DEFAULT 0 COMMENT '顺序', + `spu_id` bigint NULL DEFAULT NULL COMMENT '关联商品id', + `img_type` tinyint NOT NULL COMMENT '图片类型 0:小程序', + PRIMARY KEY (`img_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE, + INDEX `idx_spu_id`(`spu_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '轮播图'; + +-- ---------------------------- +-- Table structure for shop_detail +-- ---------------------------- +DROP TABLE IF EXISTS `shop_detail`; +CREATE TABLE `shop_detail` ( + `shop_id` bigint NOT NULL AUTO_INCREMENT COMMENT '店铺id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '店铺名称', + `intro` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '店铺简介', + `shop_logo` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '店铺logo(可修改)', + `mobile_background_pic` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '店铺移动端背景图', + `shop_status` tinyint NOT NULL COMMENT '店铺状态(-1:已删除 0: 停业中 1:营业中)', + `business_license` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '营业执照', + `identity_card_front` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '身份证正面', + `identity_card_later` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '身份证反面', + `type` tinyint NULL DEFAULT NULL COMMENT '店铺类型1自营店 2普通店', + PRIMARY KEY (`shop_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 322 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '店铺详情'; + +-- ---------------------------- +-- Table structure for shop_user +-- ---------------------------- +DROP TABLE IF EXISTS `shop_user`; +CREATE TABLE `shop_user` ( + `shop_user_id` bigint UNSIGNED NOT NULL COMMENT '商家用户id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NULL DEFAULT NULL COMMENT '关联店铺id', + `nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '昵称', + `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '员工编号', + `phone_num` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '联系方式', + `has_account` tinyint NULL DEFAULT NULL COMMENT '是否已经设置账号 0:未设置 1:已设置', + PRIMARY KEY (`shop_user_id`) USING BTREE, + INDEX `idx_shopid`(`shop_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '商家用户'; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 387 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_nacos` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_nacos; + +-- ---------------------------- +-- Table structure for config_info +-- ---------------------------- +DROP TABLE IF EXISTS `config_info`; +CREATE TABLE `config_info` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'data_id', + `group_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `content` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'content', + `md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text CHARACTER SET utf8 COLLATE utf8_bin NULL COMMENT 'source user', + `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT '租户字段', + `c_desc` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `c_use` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `effect` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `type` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `c_schema` text CHARACTER SET utf8 COLLATE utf8_bin NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_configinfo_datagrouptenant`(`data_id`, `group_id`, `tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4122 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = 'config_info'; + +-- ---------------------------- +-- Records of config_info +-- ---------------------------- +INSERT INTO `config_info` VALUES (2, 'application-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n #避免nacos取网卡出错\n cloud:\n inetutils:\n preferred-networks: 192.168.1\n datasource:\n driver-class-name: com.mysql.cj.jdbc.Driver\n type: com.zaxxer.hikari.HikariDataSource\n hikari:\n minimum-idle: 0\n maximum-pool-size: 20\n idle-timeout: 25000\n auto-commit: true\n connection-test-query: SELECT 1\n redis:\n host: 192.168.1.46\n # password: null\n jackson:\n date-format: yyyy-MM-dd HH:mm:ss\n time-zone: GMT+8\n \n#mybatis的相关配置\nmybatis:\n #mapper配置文件\n mapper-locations: classpath:mapper/*Mapper.xml\n type-aliases-package: com.mall4j.cloud.**.model\n #开启驼峰命名\n configuration:\n map-underscore-to-camel-case: true\n\nseata:\n config:\n type: nacos\n nacos:\n namespace: 4b70485d-72dd-44df-a76a-7a3f578a3001\n server-addr: ${spring.cloud.nacos.discovery.server-addr}\n password: ${spring.cloud.nacos.discovery.username}\n username: ${spring.cloud.nacos.discovery.password}\n registry:\n type: nacos\n nacos:\n server-addr: ${spring.cloud.nacos.discovery.server-addr}\n password: ${spring.cloud.nacos.discovery.username}\n username: ${spring.cloud.nacos.discovery.password}\n namespace: ${seata.config.nacos.namespace}\n\nlogging:\n level:\n root: info\n com:\n mall4cloud:\n shop: debug\n# 分页合理化,当查询到页码大于最后一页的时候,返回最后一页的数据,防止vue在最后一页删除时,数据不对的问题\npagehelper:\n reasonable: true\n\nbiz:\n oss:\n # resources-url是带有bucket的\n resources-url: http://192.168.1.46:9000/mall4cloud\n type: 1\n endpoint: http://192.168.1.46:9000\n bucket: mall4cloud\n access-key-id: admin\n access-key-secret: admin123456\n\nfeign:\n client:\n config:\n default:\n connectTimeout: 5000\n readTimeout: 5000\n loggerLevel: basic\n inside:\n key: mall4cloud-feign-inside-key\n secret: mall4cloud-feign-inside-secret\n # ip白名单,如果有需要的话,用小写逗号分割\n ips: \n\nmall4cloud:\n job:\n accessToken:\n admin:\n addresses: http://192.168.1.46:8999\n\nrocketmq:\n name-server: 192.168.1.46:9876', 'bc01547fe2e5ada0aa489dfd10a35839', '2020-09-07 05:54:23', '2021-07-31 11:20:32', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (8, 'mall4cloud-auth.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_auth}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}\n\n# 用于token aes签名的key,16位\nauth:\n token:\n signKey: -mall4-mall4-mall4- ', '56d607c86e383e27174c8b7ce2e690bb', '2020-09-07 06:05:57', '2021-07-31 11:21:33', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (13, 'mall4cloud-multishop.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_multishop}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', 'e6e62d5a4d40101dc899cc37aecf7306', '2020-09-07 06:38:01', '2021-07-31 11:21:45', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (16, 'mall4cloud-leaf.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_leaf}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', 'f416d9ca7750b8cf5b78154e008233e8', '2020-09-07 06:44:22', '2021-07-31 11:21:55', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (18, 'mall4cloud-rbac.yml', 'DEFAULT_GROUP', 'spring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_rbac}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}\n', 'd33047562436e609a37c493ad337ca85', '2020-09-07 06:47:49', '2021-07-31 11:22:04', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (31, 'mall4cloud-biz.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_biz}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}\n\nbiz:\n oss:\n # resources-url是带有bucket的\n resources-url: http://192.168.1.46:9000/mall4cloud\n # 文件上传类型 0.阿里云 1.minio\n type: 1\n endpoint: http://192.168.1.46:9000\n bucket: mall4cloud\n access-key-id: admin\n access-key-secret: admin123456', '4619160fe2cdd49f21ccc7327dfe6857', '2020-09-10 07:26:09', '2021-07-31 11:22:21', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (72, 'mall4cloud-product.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_product}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', '2d7d7afa8b27e6044377a4a3d584885a', '2020-11-11 09:35:20', '2021-07-31 11:22:45', 'nacos', '192.168.1.11', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (73, 'mall4cloud-search.yml', 'DEFAULT_GROUP', 'elastic:\n # elastic的地址\n address: http://192.168.1.46:9200', '0e8ea5c03a559a26407a21e0efcc6a97', '2020-11-12 06:57:25', '2021-04-02 15:13:25', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (84, 'mall4cloud-gateway.yml', 'DEFAULT_GROUP', 'spring:\n cloud:\n gateway:\n globalcors:\n cors-configurations:\n \'[/**]\':\n allow-credentials: true\n allowed-headers: \"*\"\n # 半个月内都允许\n max-age: 1296000\n # 测试环境,全部允许\n allowedOriginPatterns: \"*\"\n # allowedOrigins:\n # - \"http://localhost:9527\"\n # - \"http://localhost:9527\"\n # - \"http://localhost:9528\"\n # - \"http://localhost:9529\"\n # - \"http://:9527\"\n allowedMethods:\n - GET\n - POST\n - PUT\n - OPTIONS\n - DELETE\n discovery:\n locator:\n # 开启服务注册和发现\n enabled: true\n # 不手动写路由的话,swagger整合不了...\n routes:\n - id: mall4cloud-rbac\n uri: lb://mall4cloud-rbac\n predicates:\n - Path=/mall4cloud_rbac/**\n filters:\n - RewritePath=/mall4cloud_rbac(?/?.*), $\\{segment}\n - id: mall4cloud-auth\n uri: lb://mall4cloud-auth\n predicates:\n - Path=/mall4cloud_auth/**\n filters:\n - RewritePath=/mall4cloud_auth(?/?.*), $\\{segment}\n - id: mall4cloud-multishop\n uri: lb://mall4cloud-multishop\n predicates:\n - Path=/mall4cloud_multishop/**\n filters:\n - RewritePath=/mall4cloud_multishop(?/?.*), $\\{segment}\n - id: mall4cloud-biz\n uri: lb://mall4cloud-biz\n predicates:\n - Path=/mall4cloud_biz/**\n filters:\n - RewritePath=/mall4cloud_biz(?/?.*), $\\{segment}\n - id: mall4cloud-product\n uri: lb://mall4cloud-product\n predicates:\n - Path=/mall4cloud_product/**\n filters:\n - RewritePath=/mall4cloud_product(?/?.*), $\\{segment}\n - id: mall4cloud-user\n uri: lb://mall4cloud-user\n predicates:\n - Path=/mall4cloud_user/**\n filters:\n - RewritePath=/mall4cloud_user(?/?.*), $\\{segment}\n - id: mall4cloud-order\n uri: lb://mall4cloud-order\n predicates:\n - Path=/mall4cloud_order/**\n filters:\n - RewritePath=/mall4cloud_order(?/?.*), $\\{segment}\n - id: mall4cloud-delivery\n uri: lb://mall4cloud-delivery\n predicates:\n - Path=/mall4cloud_delivery/**\n filters:\n - RewritePath=/mall4cloud_delivery(?/?.*), $\\{segment}\n - id: mall4cloud-discount\n uri: lb://mall4cloud-discount\n predicates:\n - Path=/mall4cloud_discount/**\n filters:\n - RewritePath=/mall4cloud_discount(?/?.*), $\\{segment}\n - id: mall4cloud-platform\n uri: lb://mall4cloud-platform\n predicates:\n - Path=/mall4cloud_platform/**\n filters:\n - RewritePath=/mall4cloud_platform(?/?.*), $\\{segment}\n - id: mall4cloud-search\n uri: lb://mall4cloud-search\n predicates:\n - Path=/mall4cloud_search/**\n filters:\n - RewritePath=/mall4cloud_search(?/?.*), $\\{segment}\n - id: mall4cloud-coupon\n uri: lb://mall4cloud-coupon\n predicates:\n - Path=/mall4cloud_coupon/**\n filters:\n - RewritePath=/mall4cloud_coupon(?/?.*), $\\{segment}\n - id: mall4cloud-payment\n uri: lb://mall4cloud-payment\n predicates:\n - Path=/mall4cloud_payment/**\n filters:\n - RewritePath=/mall4cloud_payment(?/?.*), $\\{segment}\n - id: mall4cloud-group\n uri: lb://mall4cloud-group\n predicates:\n - Path=/mall4cloud_group/**\n filters:\n - RewritePath=/mall4cloud_group(?/?.*), $\\{segment}\n - id: mall4cloud-seckill\n uri: lb://mall4cloud-seckill\n predicates:\n - Path=/mall4cloud_seckill/**\n filters:\n - RewritePath=/mall4cloud_seckill(?/?.*), $\\{segment}\n - id: mall4cloud-flow\n uri: lb://mall4cloud-flow\n predicates:\n - Path=/mall4cloud_flow/**\n filters:\n - RewritePath=/mall4cloud_flow(?/?.*), $\\{segment}', '256d52ff10bd08ebf1cb45b6c9a2c8e2', '2020-11-19 06:49:26', '2021-05-21 07:27:40', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (3650, 'transport.type', 'SEATA_GROUP', 'TCP', 'b136ef5f6a01d816991fe3cf7a6ac763', '2020-11-30 09:43:36', '2021-03-01 07:48:05', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3651, 'transport.server', 'SEATA_GROUP', 'NIO', 'b6d9dfc0fb54277321cebc0fff55df2f', '2020-11-30 09:43:37', '2021-03-01 07:48:05', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3652, 'transport.heartbeat', 'SEATA_GROUP', 'true', 'b326b5062b2f0e69046810717534cb09', '2020-11-30 09:43:37', '2021-03-01 07:48:05', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3653, 'transport.enableClientBatchSendRequest', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:37', '2021-03-01 07:48:05', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3654, 'transport.threadFactory.bossThreadPrefix', 'SEATA_GROUP', 'NettyBoss', '0f8db59a3b7f2823f38a70c308361836', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3655, 'transport.threadFactory.workerThreadPrefix', 'SEATA_GROUP', 'NettyServerNIOWorker', 'a78ec7ef5d1631754c4e72ae8a3e9205', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3656, 'transport.threadFactory.serverExecutorThreadPrefix', 'SEATA_GROUP', 'NettyServerBizHandler', '11a36309f3d9df84fa8b59cf071fa2da', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3657, 'transport.threadFactory.shareBossWorker', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3658, 'transport.threadFactory.clientSelectorThreadPrefix', 'SEATA_GROUP', 'NettyClientSelector', 'cd7ec5a06541e75f5a7913752322c3af', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3659, 'transport.threadFactory.clientSelectorThreadSize', 'SEATA_GROUP', '1', 'c4ca4238a0b923820dcc509a6f75849b', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3660, 'transport.threadFactory.clientWorkerThreadPrefix', 'SEATA_GROUP', 'NettyClientWorkerThread', '61cf4e69a56354cf72f46dc86414a57e', '2020-11-30 09:43:37', '2021-03-01 07:48:06', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3661, 'transport.threadFactory.bossThreadSize', 'SEATA_GROUP', '1', 'c4ca4238a0b923820dcc509a6f75849b', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3662, 'transport.threadFactory.workerThreadSize', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3663, 'transport.shutdown.wait', 'SEATA_GROUP', '3', 'eccbc87e4b5ce2fe28308fd9f2a7baf3', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3664, 'service.vgroupMapping.my_test_tx_group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3665, 'service.default.grouplist', 'SEATA_GROUP', '192.168.1.46:8091', '9481e82f38d58591d24335b9ad4aecb5', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3666, 'service.enableDegrade', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3667, 'service.disableGlobalTransaction', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3668, 'client.rm.asyncCommitBufferLimit', 'SEATA_GROUP', '10000', 'b7a782741f667201b54880c925faec4b', '2020-11-30 09:43:38', '2021-03-01 07:48:07', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3669, 'client.rm.lock.retryInterval', 'SEATA_GROUP', '10', 'd3d9446802a44259755d38e6d163e820', '2020-11-30 09:43:38', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3670, 'client.rm.lock.retryTimes', 'SEATA_GROUP', '30', '34173cb38f07f89ddbebc2ac9128303f', '2020-11-30 09:43:38', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3671, 'client.rm.lock.retryPolicyBranchRollbackOnConflict', 'SEATA_GROUP', 'true', 'b326b5062b2f0e69046810717534cb09', '2020-11-30 09:43:38', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3672, 'client.rm.reportRetryCount', 'SEATA_GROUP', '5', 'e4da3b7fbbce2345d7772b0674a318d5', '2020-11-30 09:43:39', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3673, 'client.rm.tableMetaCheckEnable', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:39', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3674, 'client.rm.reportSuccessEnable', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3675, 'client.rm.sagaBranchRegisterEnable', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3676, 'client.tm.commitRetryCount', 'SEATA_GROUP', '5', 'e4da3b7fbbce2345d7772b0674a318d5', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3677, 'client.tm.rollbackRetryCount', 'SEATA_GROUP', '5', 'e4da3b7fbbce2345d7772b0674a318d5', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3678, 'client.tm.defaultGlobalTransactionTimeout', 'SEATA_GROUP', '60000', '2b4226dd7ed6eb2d419b881f3ae9c97c', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3679, 'client.tm.degradeCheck', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3680, 'client.tm.degradeCheckAllowTimes', 'SEATA_GROUP', '10', 'd3d9446802a44259755d38e6d163e820', '2020-11-30 09:43:39', '2021-03-01 07:48:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3681, 'client.tm.degradeCheckPeriod', 'SEATA_GROUP', '2000', '08f90c1a417155361a5c4b8d297e0d78', '2020-11-30 09:43:39', '2021-03-01 07:48:10', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3682, 'store.mode', 'SEATA_GROUP', 'db', 'd77d5e503ad1439f585ac494268b351b', '2020-11-30 09:43:40', '2021-03-01 07:48:10', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3683, 'store.db.datasource', 'SEATA_GROUP', 'druid', '3d650fb8a5df01600281d48c47c9fa60', '2020-11-30 09:43:40', '2021-03-01 07:48:10', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3684, 'store.db.dbType', 'SEATA_GROUP', 'mysql', '81c3b080dad537de7e10e0987a4bf52e', '2020-11-30 09:43:40', '2021-03-01 07:48:10', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3685, 'store.db.driverClassName', 'SEATA_GROUP', 'com.mysql.cj.jdbc.Driver', '33763409bb7f4838bde4fae9540433e4', '2020-11-30 09:43:40', '2021-03-18 07:22:41', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', '', '', '', 'text', ''); +INSERT INTO `config_info` VALUES (3686, 'store.db.url', 'SEATA_GROUP', 'jdbc:mysql://192.168.1.46:3306/mall4cloud_seata?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true', '2104a66c7d4bb86db640839a10d2c273', '2020-11-30 09:43:40', '2021-06-16 07:57:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', '', '', '', 'text', ''); +INSERT INTO `config_info` VALUES (3687, 'store.db.user', 'SEATA_GROUP', 'root', '63a9f0ea7bb98050796b649e85481845', '2020-11-30 09:43:40', '2021-03-01 07:48:10', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3688, 'store.db.password', 'SEATA_GROUP', 'root', '63a9f0ea7bb98050796b649e85481845', '2020-11-30 09:43:40', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3689, 'store.db.minConn', 'SEATA_GROUP', '5', 'e4da3b7fbbce2345d7772b0674a318d5', '2020-11-30 09:43:40', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3690, 'store.db.maxConn', 'SEATA_GROUP', '30', '34173cb38f07f89ddbebc2ac9128303f', '2020-11-30 09:43:40', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3691, 'store.db.globalTable', 'SEATA_GROUP', 'global_table', '8b28fb6bb4c4f984df2709381f8eba2b', '2020-11-30 09:43:40', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3692, 'store.db.branchTable', 'SEATA_GROUP', 'branch_table', '54bcdac38cf62e103fe115bcf46a660c', '2020-11-30 09:43:40', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3693, 'store.db.queryLimit', 'SEATA_GROUP', '100', 'f899139df5e1059396431415e770c6dd', '2020-11-30 09:43:41', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3694, 'store.db.lockTable', 'SEATA_GROUP', 'lock_table', '55e0cae3b6dc6696b768db90098b8f2f', '2020-11-30 09:43:41', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3695, 'store.db.maxWait', 'SEATA_GROUP', '5000', 'a35fe7f7fe8217b4369a0af4244d1fca', '2020-11-30 09:43:41', '2021-03-01 07:48:11', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3696, 'server.recovery.committingRetryPeriod', 'SEATA_GROUP', '1000', 'a9b7ba70783b617e9998dc4dd82eb3c5', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3697, 'server.recovery.asynCommittingRetryPeriod', 'SEATA_GROUP', '1000', 'a9b7ba70783b617e9998dc4dd82eb3c5', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3698, 'server.recovery.rollbackingRetryPeriod', 'SEATA_GROUP', '1000', 'a9b7ba70783b617e9998dc4dd82eb3c5', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3699, 'server.recovery.timeoutRetryPeriod', 'SEATA_GROUP', '1000', 'a9b7ba70783b617e9998dc4dd82eb3c5', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3700, 'server.maxCommitRetryTimeout', 'SEATA_GROUP', '-1', '6bb61e3b7bce0931da574d19d1d82c88', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3701, 'server.maxRollbackRetryTimeout', 'SEATA_GROUP', '-1', '6bb61e3b7bce0931da574d19d1d82c88', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3702, 'server.rollbackRetryTimeoutUnlockEnable', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:41', '2021-03-01 07:48:12', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3703, 'client.undo.dataValidation', 'SEATA_GROUP', 'true', 'b326b5062b2f0e69046810717534cb09', '2020-11-30 09:43:41', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3704, 'client.undo.logSerialization', 'SEATA_GROUP', 'jackson', 'b41779690b83f182acc67d6388c7bac9', '2020-11-30 09:43:42', '2021-03-01 08:07:28', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', '', '', '', 'text', ''); +INSERT INTO `config_info` VALUES (3705, 'client.undo.onlyCareUpdateColumns', 'SEATA_GROUP', 'true', 'b326b5062b2f0e69046810717534cb09', '2020-11-30 09:43:42', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3706, 'server.undo.logSaveDays', 'SEATA_GROUP', '7', '8f14e45fceea167a5a36dedd4bea2543', '2020-11-30 09:43:42', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3707, 'server.undo.logDeletePeriod', 'SEATA_GROUP', '86400000', 'f4c122804fe9076cb2710f55c3c6e346', '2020-11-30 09:43:42', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3708, 'client.undo.logTable', 'SEATA_GROUP', 'undo_log', '2842d229c24afe9e61437135e8306614', '2020-11-30 09:43:42', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3709, 'log.exceptionRate', 'SEATA_GROUP', '100', 'f899139df5e1059396431415e770c6dd', '2020-11-30 09:43:42', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3710, 'transport.serialization', 'SEATA_GROUP', 'seata', 'b943081c423b9a5416a706524ee05d40', '2020-11-30 09:43:42', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3711, 'transport.compressor', 'SEATA_GROUP', 'none', '334c4a4c42fdb79d7ebc3e73b517e6f8', '2020-11-30 09:43:42', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3712, 'metrics.enabled', 'SEATA_GROUP', 'false', '68934a3e9455fa72420237eb05902327', '2020-11-30 09:43:42', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3713, 'metrics.registryType', 'SEATA_GROUP', 'compact', '7cf74ca49c304df8150205fc915cd465', '2020-11-30 09:43:43', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3714, 'metrics.exporterList', 'SEATA_GROUP', 'prometheus', 'e4f00638b8a10e6994e67af2f832d51c', '2020-11-30 09:43:43', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3715, 'metrics.exporterPrometheusPort', 'SEATA_GROUP', '9898', '7b9dc501afe4ee11c56a4831e20cee71', '2020-11-30 09:43:43', '2021-03-01 07:48:15', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3716, 'service.vgroupMapping.mall4cloud-auth-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-11-30 09:55:42', '2020-11-30 09:55:42', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3717, 'service.vgroupMapping.mall4cloud-multishop-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-11-30 09:57:03', '2020-11-30 09:57:03', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3718, 'service.vgroupMapping.mall4cloud-rbac-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-11-30 09:57:17', '2020-11-30 09:57:17', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3720, 'service.vgroupMapping.mall4cloud-product-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-01 05:46:53', '2020-12-01 05:46:53', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3721, 'service.vgroupMapping.mall4cloud-biz-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-01 05:47:17', '2020-12-01 05:47:17', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3722, 'service.vgroupMapping.mall4cloud-leaf-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-01 05:47:44', '2020-12-01 05:47:44', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3723, 'service.vgroupMapping.mall4cloud-order-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-01 05:48:00', '2020-12-01 05:48:00', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3724, 'service.vgroupMapping.mall4cloud-user-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-01 05:48:15', '2020-12-01 05:48:15', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3727, 'mall4cloud-order.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_order}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', 'c33dca13b41bb90b2477f9ea7228ff48', '2020-12-04 05:45:13', '2021-04-02 12:53:44', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (3736, 'mall4cloud-user.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_user}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', 'a2c60ea7887ebb8a0f6f5f5005048b4b', '2020-12-04 05:51:25', '2021-04-02 12:54:49', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (3741, 'service.vgroupMapping.mall4cloud-coupon-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-09 01:49:55', '2020-12-09 01:49:55', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3747, 'service.vgroupMapping.mall4cloud-delivery-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-14 03:30:09', '2020-12-14 03:30:09', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3755, 'service.vgroupMapping.mall4cloud-discount-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-16 06:13:14', '2020-12-16 06:13:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3759, 'mall4cloud-platform.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_platform}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}', '274a64e67ef4fab65de1e7b70e0a9ff7', '2020-12-21 07:38:16', '2021-04-02 12:55:59', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (3761, 'service.vgroupMapping.mall4cloud-platform-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-21 07:41:01', '2020-12-21 07:41:01', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3762, 'service.vgroupMapping.mall4cloud-search-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2020-12-22 02:19:51', '2020-12-22 02:19:51', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (3812, 'mall4cloud-payment.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n url: jdbc:mysql://${MYSQL_HOST:192.168.1.46}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:mall4cloud_payment}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true&useAffectedRows=true\n username: ${MYSQL_USERNAME:root}\n password: ${MYSQL_PASSWORD:root}\napplication:\n domainUrl: http://47.112.182.96:8126/mall4cloud_payment', '2f4b89c096ab0cef9359c98b91ade543', '2021-02-03 03:19:16', '2021-04-02 12:56:14', NULL, '', '', '', '', '', '', 'yaml', ''); +INSERT INTO `config_info` VALUES (3814, 'service.vgroupMapping.mall4cloud-payment-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2021-02-03 03:20:03', '2021-02-03 03:20:03', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4003, 'client.rm.tableMetaCheckerInterval', 'SEATA_GROUP', '60000', '2b4226dd7ed6eb2d419b881f3ae9c97c', '2021-03-01 07:48:08', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4004, 'client.rm.sqlParserType', 'SEATA_GROUP', 'druid', '3d650fb8a5df01600281d48c47c9fa60', '2021-03-01 07:48:08', '2021-03-01 07:48:08', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4040, 'client.undo.compress.enable', 'SEATA_GROUP', 'true', 'b326b5062b2f0e69046810717534cb09', '2021-03-01 07:48:13', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4041, 'client.undo.compress.type', 'SEATA_GROUP', 'zip', 'adcdbd79a8d84175c229b192aadc02f2', '2021-03-01 07:48:13', '2021-03-01 07:48:13', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4042, 'client.undo.compress.threshold', 'SEATA_GROUP', '64k', 'bd44a6458bdbff0b5cac721ba361f035', '2021-03-01 07:48:14', '2021-03-01 07:48:14', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4065, 'service.vgroupMapping.mall4cloud-group-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2021-03-20 03:01:47', '2021-03-20 03:01:47', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4083, 'service.vgroupMapping.mall4cloud-seckill-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2021-04-01 06:18:16', '2021-04-01 06:18:16', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); +INSERT INTO `config_info` VALUES (4115, 'service.vgroupMapping.mall4cloud-flow-seata-service-group', 'SEATA_GROUP', 'default', 'c21f969b5f03d33d43e04f8f136e7682', '2021-05-22 02:36:39', '2021-05-22 02:36:39', NULL, '', '', '4b70485d-72dd-44df-a76a-7a3f578a3001', NULL, NULL, NULL, 'text', NULL); + + +-- ---------------------------- +-- Table structure for config_info_aggr +-- ---------------------------- +DROP TABLE IF EXISTS `config_info_aggr`; +CREATE TABLE `config_info_aggr` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'data_id', + `group_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'group_id', + `datum_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'datum_id', + `content` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '内容', + `gmt_modified` datetime NOT NULL COMMENT '修改时间', + `app_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT '租户字段', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_configinfoaggr_datagrouptenantdatum`(`data_id`, `group_id`, `tenant_id`, `datum_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '增加租户字段'; + +-- ---------------------------- +-- Records of config_info_aggr +-- ---------------------------- + +-- ---------------------------- +-- Table structure for config_info_beta +-- ---------------------------- +DROP TABLE IF EXISTS `config_info_beta`; +CREATE TABLE `config_info_beta` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'data_id', + `group_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'group_id', + `app_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'app_name', + `content` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'content', + `beta_ips` varchar(1024) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'betaIps', + `md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text CHARACTER SET utf8 COLLATE utf8_bin NULL COMMENT 'source user', + `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip', + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT '租户字段', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_configinfobeta_datagrouptenant`(`data_id`, `group_id`, `tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = 'config_info_beta'; + +-- ---------------------------- +-- Records of config_info_beta +-- ---------------------------- + +-- ---------------------------- +-- Table structure for config_info_tag +-- ---------------------------- +DROP TABLE IF EXISTS `config_info_tag`; +CREATE TABLE `config_info_tag` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'data_id', + `group_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT 'tenant_id', + `tag_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'tag_id', + `app_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'app_name', + `content` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'content', + `md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text CHARACTER SET utf8 COLLATE utf8_bin NULL COMMENT 'source user', + `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_configinfotag_datagrouptenanttag`(`data_id`, `group_id`, `tenant_id`, `tag_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = 'config_info_tag'; + +-- ---------------------------- +-- Records of config_info_tag +-- ---------------------------- + +-- ---------------------------- +-- Table structure for config_tags_relation +-- ---------------------------- +DROP TABLE IF EXISTS `config_tags_relation`; +CREATE TABLE `config_tags_relation` ( + `id` bigint NOT NULL COMMENT 'id', + `tag_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'data_id', + `group_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT 'tenant_id', + `nid` bigint NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`nid`) USING BTREE, + UNIQUE INDEX `uk_configtagrelation_configidtag`(`id`, `tag_name`, `tag_type`) USING BTREE, + INDEX `idx_tenant_id`(`tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = 'config_tag_relation'; + +-- ---------------------------- +-- Records of config_tags_relation +-- ---------------------------- + +-- ---------------------------- +-- Table structure for group_capacity +-- ---------------------------- +DROP TABLE IF EXISTS `group_capacity`; +CREATE TABLE `group_capacity` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `group_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', + `quota` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '配额,0表示使用默认值', + `usage` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用量', + `max_size` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '聚合子配置最大个数,,0表示使用默认值', + `max_aggr_size` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_group_id`(`group_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '集群、各Group容量信息表'; + +-- ---------------------------- +-- Records of group_capacity +-- ---------------------------- + +-- ---------------------------- +-- Table structure for his_config_info +-- ---------------------------- +DROP TABLE IF EXISTS `his_config_info`; +CREATE TABLE `his_config_info` ( + `id` bigint UNSIGNED NOT NULL, + `nid` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `data_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `group_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `app_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'app_name', + `content` longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `src_user` text CHARACTER SET utf8 COLLATE utf8_bin NULL, + `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `op_type` char(10) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT '租户字段', + PRIMARY KEY (`nid`) USING BTREE, + INDEX `idx_gmt_create`(`gmt_create`) USING BTREE, + INDEX `idx_gmt_modified`(`gmt_modified`) USING BTREE, + INDEX `idx_did`(`data_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4379 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '多租户改造'; + +-- ---------------------------- +-- Records of his_config_info +-- ---------------------------- + + +-- ---------------------------- +-- Table structure for permissions +-- ---------------------------- +DROP TABLE IF EXISTS `permissions`; +CREATE TABLE `permissions` ( + `role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `resource` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `action` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + UNIQUE INDEX `uk_role_permission`(`role`, `resource`, `action`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of permissions +-- ---------------------------- + +-- ---------------------------- +-- Table structure for roles +-- ---------------------------- +DROP TABLE IF EXISTS `roles`; +CREATE TABLE `roles` ( + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + UNIQUE INDEX `idx_user_role`(`username`, `role`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of roles +-- ---------------------------- +INSERT INTO `roles` VALUES ('nacos', 'ROLE_ADMIN'); + +-- ---------------------------- +-- Table structure for tenant_capacity +-- ---------------------------- +DROP TABLE IF EXISTS `tenant_capacity`; +CREATE TABLE `tenant_capacity` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `quota` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '配额,0表示使用默认值', + `usage` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用量', + `max_size` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '聚合子配置最大个数', + `max_aggr_size` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_tenant_id`(`tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '租户容量信息表'; + +-- ---------------------------- +-- Records of tenant_capacity +-- ---------------------------- + +-- ---------------------------- +-- Table structure for tenant_info +-- ---------------------------- +DROP TABLE IF EXISTS `tenant_info`; +CREATE TABLE `tenant_info` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT 'tenant_id', + `tenant_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint NOT NULL COMMENT '创建时间', + `gmt_modified` bigint NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_tenant_info_kptenantid`(`kp`, `tenant_id`) USING BTREE, + INDEX `idx_tenant_id`(`tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = 'tenant_info'; + +-- ---------------------------- +-- Records of tenant_info +-- ---------------------------- +INSERT INTO `tenant_info` VALUES (1, '1', '4b70485d-72dd-44df-a76a-7a3f578a3001', 'seata', 'seata', 'nacos', 1606728824253, 1606728824253); + +-- ---------------------------- +-- Table structure for users +-- ---------------------------- +DROP TABLE IF EXISTS `users`; +CREATE TABLE `users` ( + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `password` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `enabled` tinyint(1) NOT NULL, + PRIMARY KEY (`username`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of users +-- ---------------------------- +INSERT INTO `users` VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', 1); + + +create database IF NOT EXISTS `mall4cloud_order` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_order; + + +-- ---------------------------- +-- Table structure for order +-- ---------------------------- +DROP TABLE IF EXISTS `order`; +CREATE TABLE `order` ( + `order_id` bigint UNSIGNED NOT NULL COMMENT '订单ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NULL DEFAULT NULL COMMENT '店铺id', + `user_id` bigint NOT NULL COMMENT '用户ID', + `delivery_type` tinyint NULL DEFAULT NULL COMMENT '配送类型:无需快递', + `shop_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '店铺名称', + `total` bigint NOT NULL COMMENT '总值', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '订单状态 1:待付款 2:待发货 3:待收货(已发货) 5:成功 6:失败', + `all_count` int NULL DEFAULT NULL COMMENT '订单商品总数', + `pay_time` datetime NULL DEFAULT NULL COMMENT '付款时间', + `delivery_time` datetime NULL DEFAULT NULL COMMENT '发货时间', + `finally_time` datetime NULL DEFAULT NULL COMMENT '完成时间', + `settled_time` datetime NULL DEFAULT NULL COMMENT '结算时间', + `cancel_time` datetime NULL DEFAULT NULL COMMENT '取消时间', + `is_payed` tinyint(1) NULL DEFAULT NULL COMMENT '是否已支付,1.已支付0.未支付', + `close_type` tinyint NULL DEFAULT NULL COMMENT '订单关闭原因 1-超时未支付 4-买家取消 15-已通过货到付款交易', + `delete_status` tinyint NULL DEFAULT 0 COMMENT '用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除', + `version` int NULL DEFAULT NULL COMMENT '订单版本号,每处理一次订单,版本号+1', + `order_addr_id` bigint NULL DEFAULT NULL COMMENT '用户订单地址id', + PRIMARY KEY (`order_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE, + INDEX `idx_finally_time`(`finally_time`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单信息'; + +-- ---------------------------- +-- Table structure for order_addr +-- ---------------------------- +DROP TABLE IF EXISTS `order_addr`; +CREATE TABLE `order_addr` ( + `order_addr_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `user_id` bigint NULL DEFAULT NULL COMMENT '用户ID', + `consignee` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收货人', + `province_id` bigint NULL DEFAULT NULL COMMENT '省ID', + `province` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '省', + `city_id` bigint NULL DEFAULT NULL COMMENT '城市ID', + `city` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '城市', + `area_id` bigint NULL DEFAULT NULL COMMENT '区域ID', + `area` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '区', + `addr` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '地址', + `post_code` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮编', + `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机', + `lng` decimal(12, 6) NULL DEFAULT NULL COMMENT '经度', + `lat` decimal(12, 6) NULL DEFAULT NULL COMMENT '纬度', + PRIMARY KEY (`order_addr_id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 5192 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户订单配送地址'; + +-- ---------------------------- +-- Table structure for order_item +-- ---------------------------- +DROP TABLE IF EXISTS `order_item`; +CREATE TABLE `order_item` ( + `order_item_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单项ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NOT NULL COMMENT '店铺id', + `order_id` bigint NOT NULL COMMENT '订单id', + `category_id` bigint NULL DEFAULT NULL COMMENT '分类id', + `spu_id` bigint UNSIGNED NOT NULL COMMENT '产品ID', + `sku_id` bigint UNSIGNED NOT NULL COMMENT '产品SkuID', + `user_id` bigint NOT NULL COMMENT '用户Id', + `count` int NULL DEFAULT 0 COMMENT '购物车产品个数', + `spu_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '产品名称', + `sku_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'sku名称', + `pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '产品主图片路径', + `delivery_type` tinyint NULL DEFAULT NULL COMMENT '单个orderItem的配送类型3:无需快递', + `shop_cart_time` datetime NULL DEFAULT NULL COMMENT '加入购物车时间', + `price` bigint NOT NULL COMMENT '产品价格', + `spu_total_amount` bigint NOT NULL COMMENT '商品总金额', + PRIMARY KEY (`order_item_id`) USING BTREE, + INDEX `idx_order_id`(`order_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE, + INDEX `idx_spu_id`(`spu_id`) USING BTREE, + INDEX `idx_sku_id`(`sku_id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4805 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单项'; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_payment` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_payment; + + +-- ---------------------------- +-- Table structure for pay_info +-- ---------------------------- +DROP TABLE IF EXISTS `pay_info`; +CREATE TABLE `pay_info` ( + `pay_id` bigint UNSIGNED NOT NULL COMMENT '支付单号', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `user_id` bigint NULL DEFAULT NULL COMMENT '用户id', + `order_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '本次支付关联的多个订单号', + `biz_pay_no` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '外部订单流水号', + `sys_type` tinyint NULL DEFAULT NULL COMMENT '系统类型 见SysTypeEnum', + `pay_status` tinyint NULL DEFAULT NULL COMMENT '支付状态', + `pay_amount` bigint NULL DEFAULT NULL COMMENT '支付金额', + `version` int NULL DEFAULT NULL COMMENT '版本号', + `callback_content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '回调内容', + `callback_time` datetime NULL DEFAULT NULL COMMENT '回调时间', + `confirm_time` datetime NULL DEFAULT NULL COMMENT '确认时间', + PRIMARY KEY (`pay_id`) USING BTREE, + INDEX `idx_biz_pay_no`(`biz_pay_no`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE, + INDEX `idx_order_ids`(`order_ids`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单支付记录'; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_platform` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_platform; + + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `param_key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'key', + `param_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'value', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `key`(`param_key`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 111 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统配置信息表'; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `sys_user_id` bigint UNSIGNED NOT NULL COMMENT '平台用户id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '昵称', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头像', + `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '员工编号', + `phone_num` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '联系方式', + `has_account` tinyint NULL DEFAULT NULL COMMENT '是否已经设置账号', + PRIMARY KEY (`sys_user_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '平台用户'; + +INSERT INTO `sys_user` VALUES (1, '2021-06-24 16:59:33', '2021-07-03 11:17:41', 'admin', '/2021/07/02/dd9ff362454d43d184c6d722dc14d7f6', '9527', '12345678910', 1); + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 325 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + + +create database IF NOT EXISTS `mall4cloud_product` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_product; + + +-- ---------------------------- +-- Table structure for attr +-- ---------------------------- +DROP TABLE IF EXISTS `attr`; +CREATE TABLE `attr` ( + `attr_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'attr id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NOT NULL DEFAULT 0 COMMENT '店铺Id', + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '属性名称', + `desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '属性描述', + `search_type` tinyint NOT NULL DEFAULT 0 COMMENT '0:不需要,1:需要', + `attr_type` tinyint NOT NULL DEFAULT 0 COMMENT '0:销售属性,1:基本属性', + PRIMARY KEY (`attr_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 7145 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '属性信息'; + +-- ---------------------------- +-- Table structure for attr_category +-- ---------------------------- +DROP TABLE IF EXISTS `attr_category`; +CREATE TABLE `attr_category` ( + `attr_category_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '属性与分类关联id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `category_id` bigint UNSIGNED NOT NULL COMMENT '分类id', + `attr_id` bigint NOT NULL COMMENT '属性id', + PRIMARY KEY (`attr_category_id`) USING BTREE, + UNIQUE INDEX `uni_attrgroup_id`(`category_id`, `attr_id`) USING BTREE, + INDEX `idx_attr_id`(`attr_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 19015 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '属性与分类关联信息'; + +-- ---------------------------- +-- Table structure for attr_value +-- ---------------------------- +DROP TABLE IF EXISTS `attr_value`; +CREATE TABLE `attr_value` ( + `attr_value_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '属性id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `attr_id` bigint UNSIGNED NOT NULL COMMENT '属性ID', + `value` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '属性值', + PRIMARY KEY (`attr_value_id`) USING BTREE, + INDEX `idx_attr`(`attr_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 58234 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '属性值信息'; + +-- ---------------------------- +-- Table structure for brand +-- ---------------------------- +DROP TABLE IF EXISTS `brand`; +CREATE TABLE `brand` ( + `brand_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'brand_id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '品牌名称', + `desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '品牌描述', + `img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '品牌logo图片', + `first_letter` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '检索首字母', + `seq` int NULL DEFAULT NULL COMMENT '排序', + `status` tinyint UNSIGNED NULL DEFAULT 0 COMMENT '状态 1:enable, 0:disable, -1:deleted', + PRIMARY KEY (`brand_id`) USING BTREE, + UNIQUE INDEX `uni_brand_id`(`brand_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 152 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '品牌信息'; + +-- ---------------------------- +-- Table structure for category +-- ---------------------------- +DROP TABLE IF EXISTS `category`; +CREATE TABLE `category` ( + `category_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分类id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint UNSIGNED NOT NULL COMMENT '店铺id', + `parent_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '父ID', + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类名称', + `desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类描述', + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '分类地址{parent_id}-{child_id},...', + `status` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态 1:enable, 0:disable, -1:deleted', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类图标', + `img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分类的显示图片', + `level` int NOT NULL COMMENT '分类层级 从0开始', + `seq` int NULL DEFAULT NULL COMMENT '排序', + PRIMARY KEY (`category_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE, + INDEX `idx_pid`(`parent_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 100380 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分类信息'; + +-- ---------------------------- +-- Table structure for category_brand +-- ---------------------------- +DROP TABLE IF EXISTS `category_brand`; +CREATE TABLE `category_brand` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `brand_id` bigint UNSIGNED NOT NULL COMMENT '品牌id', + `category_id` bigint UNSIGNED NOT NULL COMMENT '分类id', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uni_brand_category_id`(`brand_id`, `category_id`) USING BTREE, + INDEX `idx_category_id`(`category_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '品牌分类关联信息'; + +-- ---------------------------- +-- Table structure for shop_cart_item +-- ---------------------------- +DROP TABLE IF EXISTS `shop_cart_item`; +CREATE TABLE `shop_cart_item` ( + `cart_item_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NOT NULL COMMENT '店铺ID', + `spu_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '产品ID', + `sku_id` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT 'SkuID', + `user_id` bigint UNSIGNED NOT NULL COMMENT '用户ID', + `count` int NOT NULL DEFAULT 0 COMMENT '购物车产品个数', + `price_fee` bigint UNSIGNED NOT NULL COMMENT '售价,加入购物车时的商品价格', + `is_checked` tinyint NULL DEFAULT NULL COMMENT '是否已勾选', + PRIMARY KEY (`cart_item_id`) USING BTREE, + UNIQUE INDEX `uk_user_shop_sku`(`sku_id`, `user_id`, `shop_id`) USING BTREE, + INDEX `idx_shop_id`(`shop_id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '购物车'; + +-- ---------------------------- +-- Table structure for sku +-- ---------------------------- +DROP TABLE IF EXISTS `sku`; +CREATE TABLE `sku` ( + `sku_id` bigint NOT NULL AUTO_INCREMENT COMMENT '属性id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `spu_id` bigint NOT NULL COMMENT 'SPU id', + `sku_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'sku名称', + `attrs` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '多个销售属性值id逗号分隔', + `img_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'sku图片', + `price_fee` bigint NOT NULL DEFAULT 0 COMMENT '售价,整数方式保存', + `market_price_fee` bigint NOT NULL DEFAULT 0 COMMENT '市场价,整数方式保存', + `party_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品编码', + `model_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品条形码', + `weight` decimal(15, 3) NULL DEFAULT NULL COMMENT '商品重量', + `volume` decimal(15, 3) NULL DEFAULT NULL COMMENT '商品体积', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态 1:enable, 0:disable, -1:deleted', + PRIMARY KEY (`sku_id`) USING BTREE, + INDEX `idx_spuid`(`spu_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3523 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'sku信息'; + +-- ---------------------------- +-- Table structure for sku_stock +-- ---------------------------- +DROP TABLE IF EXISTS `sku_stock`; +CREATE TABLE `sku_stock` ( + `stock_id` bigint NOT NULL AUTO_INCREMENT COMMENT '库存id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `sku_id` bigint UNSIGNED NOT NULL COMMENT 'SKU ID', + `actual_stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '实际库存', + `lock_stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '锁定库存', + `stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '可售卖库存', + PRIMARY KEY (`stock_id`) USING BTREE, + INDEX `idx_skuid`(`sku_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3378 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '库存信息'; + +-- ---------------------------- +-- Table structure for sku_stock_lock +-- ---------------------------- +DROP TABLE IF EXISTS `sku_stock_lock`; +CREATE TABLE `sku_stock_lock` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `spu_id` bigint NULL DEFAULT NULL COMMENT '商品id', + `sku_id` bigint NULL DEFAULT NULL COMMENT 'sku id', + `order_id` bigint NULL DEFAULT NULL COMMENT '订单id', + `status` tinyint NULL DEFAULT NULL COMMENT '状态-1已解锁 0待确定 1已锁定', + `count` int NULL DEFAULT NULL COMMENT '锁定库存数量', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uni_spu_sku_order`(`spu_id`, `sku_id`, `order_id`) USING BTREE, + INDEX `idx_sku_id`(`sku_id`) USING BTREE, + INDEX `idx_order_id`(`order_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '库存锁定信息'; + +-- ---------------------------- +-- Table structure for spu +-- ---------------------------- +DROP TABLE IF EXISTS `spu`; +CREATE TABLE `spu` ( + `spu_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'spu id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `brand_id` bigint NULL DEFAULT NULL COMMENT '品牌ID', + `category_id` bigint NOT NULL COMMENT '分类ID', + `shop_category_id` bigint NOT NULL DEFAULT 0 COMMENT '店铺分类ID', + `shop_id` bigint NOT NULL COMMENT '店铺id', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品名称', + `selling_point` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '卖点', + `main_img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品介绍主图', + `img_urls` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品图片 多个图片逗号分隔', + `video` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '商品视频', + `price_fee` bigint NOT NULL DEFAULT 0 COMMENT '售价,整数方式保存', + `market_price_fee` bigint NOT NULL DEFAULT 0 COMMENT '市场价,整数方式保存', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态 -1:删除, 0:下架, 1:上架', + `has_sku_img` tinyint NOT NULL DEFAULT 0 COMMENT 'sku是否含有图片 0无 1有', + `seq` smallint NOT NULL DEFAULT 3 COMMENT '序号', + PRIMARY KEY (`spu_id`) USING BTREE, + INDEX `idx_brandid`(`brand_id`) USING BTREE, + INDEX `idx_catid`(`category_id`) USING BTREE, + INDEX `idx_shopid`(`shop_id`) USING BTREE, + INDEX `idx_shop_catid`(`shop_category_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 575 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'spu信息'; + +-- ---------------------------- +-- Table structure for spu_attr_value +-- ---------------------------- +DROP TABLE IF EXISTS `spu_attr_value`; +CREATE TABLE `spu_attr_value` ( + `spu_attr_value_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品属性值关联信息id', + `spu_id` bigint UNSIGNED NOT NULL COMMENT '商品id', + `attr_id` bigint UNSIGNED NOT NULL COMMENT '规格属性id', + `attr_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格属性名称', + `attr_value_id` bigint NULL DEFAULT NULL COMMENT '规格属性值id', + `attr_value_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格属性值名称', + `attr_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格属性描述', + PRIMARY KEY (`spu_attr_value_id`) USING BTREE, + UNIQUE INDEX `uni_spuid`(`spu_id`, `attr_id`) USING BTREE, + INDEX `idx_attrid`(`attr_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 22489 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品规格属性关联信息'; + +-- ---------------------------- +-- Table structure for spu_detail +-- ---------------------------- +DROP TABLE IF EXISTS `spu_detail`; +CREATE TABLE `spu_detail` ( + `spu_id` bigint NOT NULL COMMENT '商品id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `detail` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '商品详情', + PRIMARY KEY (`spu_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品详情信息'; + +-- ---------------------------- +-- Table structure for spu_extension +-- ---------------------------- +DROP TABLE IF EXISTS `spu_extension`; +CREATE TABLE `spu_extension` ( + `spu_extend_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品扩展信息表id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `spu_id` bigint UNSIGNED NOT NULL COMMENT '商品id', + `sale_num` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '销量', + `actual_stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '实际库存', + `lock_stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '锁定库存', + `stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '可售卖库存', + PRIMARY KEY (`spu_extend_id`) USING BTREE, + INDEX `idx_spu`(`spu_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 540 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci; + +-- ---------------------------- +-- Table structure for spu_sku_attr_value +-- ---------------------------- +DROP TABLE IF EXISTS `spu_sku_attr_value`; +CREATE TABLE `spu_sku_attr_value` ( + `spu_sku_attr_id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品sku销售属性关联信息id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `spu_id` bigint NOT NULL DEFAULT 0 COMMENT 'SPU ID', + `sku_id` bigint NOT NULL DEFAULT 0 COMMENT 'SKU ID', + `attr_id` int NULL DEFAULT 0 COMMENT '销售属性ID', + `attr_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '销售属性名称', + `attr_value_id` int NULL DEFAULT 0 COMMENT '销售属性值ID', + `attr_value_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '销售属性值', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态 1:enable, 0:disable, -1:deleted', + PRIMARY KEY (`spu_sku_attr_id`) USING BTREE, + INDEX `idx_spuid`(`spu_id`) USING BTREE, + INDEX `idx_skuid`(`sku_id`) USING BTREE, + INDEX `idx_attrid`(`attr_id`) USING BTREE, + INDEX `idx_attrvalueid`(`attr_value_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 11803 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品sku销售属性关联信息'; + +-- ---------------------------- +-- Table structure for spu_tag +-- ---------------------------- +DROP TABLE IF EXISTS `spu_tag`; +CREATE TABLE `spu_tag` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '分组标签id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + `title` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '分组标题', + `shop_id` bigint NULL DEFAULT NULL COMMENT '店铺Id', + `status` tinyint(1) NULL DEFAULT NULL COMMENT '状态(1为正常,-1为删除)', + `is_default` tinyint(1) NULL DEFAULT NULL COMMENT '默认类型(0:商家自定义,1:系统默认)', + `prod_count` bigint NULL DEFAULT NULL COMMENT '商品数量', + `style` int NULL DEFAULT NULL COMMENT '列表样式(0:一列一个,1:一列两个,2:一列三个)', + `seq` int NULL DEFAULT NULL COMMENT '排序', + `delete_time` datetime NULL DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 65 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品分组表'; + +-- ---------------------------- +-- Table structure for spu_tag_reference +-- ---------------------------- +DROP TABLE IF EXISTS `spu_tag_reference`; +CREATE TABLE `spu_tag_reference` ( + `reference_id` bigint NOT NULL AUTO_INCREMENT COMMENT '分组引用id', + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `shop_id` bigint NULL DEFAULT NULL COMMENT '店铺id', + `tag_id` bigint NULL DEFAULT NULL COMMENT '标签id', + `spu_id` bigint NULL DEFAULT NULL COMMENT '商品id', + `status` tinyint(1) NULL DEFAULT NULL COMMENT '状态(1:正常,-1:删除)', + `seq` int NULL DEFAULT NULL COMMENT '排序', + PRIMARY KEY (`reference_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 367 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品分组标签关联信息'; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 254 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci; + + +create database IF NOT EXISTS `mall4cloud_rbac` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_rbac; + + +-- ---------------------------- +-- Table structure for menu +-- ---------------------------- +DROP TABLE IF EXISTS `menu`; +CREATE TABLE `menu` ( + `menu_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '菜单id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `parent_id` bigint UNSIGNED NOT NULL COMMENT '父菜单ID,一级菜单为0', + `biz_type` tinyint NULL DEFAULT NULL COMMENT '业务类型 1 店铺菜单 2平台菜单', + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限,需要有哪个权限才能访问该菜单', + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '路径 就像uri', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '1.\'Layout\' 为布局,不会跳页面 2.\'components-demo/tinymce\' 跳转到该页面', + `redirect` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '当设置 noRedirect 的时候该路由在面包屑导航中不可被点击', + `always_show` tinyint NULL DEFAULT NULL COMMENT '一直显示根路由', + `hidden` tinyint NULL DEFAULT NULL COMMENT '当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1', + `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设定路由的名字,一定要填写不然使用时会出现各种问题', + `title` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设置该路由在侧边栏和面包屑中展示的名字', + `icon` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon', + `no_cache` tinyint NULL DEFAULT NULL COMMENT '如果设置为true,则不会被 缓存(默认 false)', + `breadcrumb` tinyint NULL DEFAULT NULL COMMENT '如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)', + `affix` tinyint NULL DEFAULT NULL COMMENT '若果设置为true,它则会固定在tags-view中(默认 false)', + `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '当路由设置了该属性,则会高亮相对应的侧边栏。', + `seq` int NULL DEFAULT NULL COMMENT '排序,越小越靠前', + PRIMARY KEY (`menu_id`) USING BTREE, + INDEX `idx_pid`(`parent_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 328 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '菜单管理'; + +-- ---------------------------- +-- Records of menu +-- ---------------------------- +INSERT INTO `menu` VALUES (111, '2020-12-01 14:20:42', '2021-01-28 10:12:08', 0, 1, NULL, '/rbac', 'Layout', NULL, NULL, 0, '权限管理', '权限管理', 'el-icon-office-building', NULL, NULL, NULL, NULL, 99); +INSERT INTO `menu` VALUES (112, '2020-12-01 14:22:09', '2021-01-28 10:12:14', 111, 1, NULL, '/multishop/shop_user', 'multishop/shop-user', NULL, NULL, 0, '用户管理', '用户管理', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (134, '2020-12-03 16:49:48', '2021-01-28 10:12:22', 111, 1, NULL, 'role', 'rbac/role', NULL, NULL, 0, '角色管理', '角色管理', NULL, NULL, NULL, NULL, NULL, 1); +INSERT INTO `menu` VALUES (137, '2020-12-03 17:54:02', '2021-01-28 10:13:07', 141, 1, NULL, 'attr', 'product/attr', NULL, NULL, 0, '属性管理', '属性管理', NULL, NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (139, '2020-12-03 17:54:02', '2021-01-28 10:13:20', 141, 1, NULL, 'category', 'product/category', NULL, NULL, 0, 'category', '分类管理', NULL, NULL, NULL, NULL, NULL, 2); +INSERT INTO `menu` VALUES (141, '2020-12-03 17:55:22', '2021-01-01 09:01:48', 0, 1, NULL, '/product', 'Layout', NULL, NULL, 0, '商品管理', '商品管理', 'product', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (142, '2020-12-03 17:54:02', '2021-03-12 09:31:52', 141, 1, NULL, 'prod_info', 'product/prod-info', NULL, NULL, 0, '发布商品', '发布商品', NULL, NULL, NULL, NULL, NULL, 4); +INSERT INTO `menu` VALUES (145, '2020-12-04 10:08:17', '2021-01-28 10:13:47', 0, 1, NULL, '/multishop', 'Layout', NULL, NULL, 0, '店铺管理', '店铺管理', 'el-icon-house', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (146, '2020-12-04 10:09:38', '2021-01-28 10:13:53', 145, 1, NULL, 'index_img', 'multishop/index-img', NULL, NULL, 0, '轮播图管理', '轮播图管理', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (147, '2020-12-04 11:30:59', '2021-01-01 09:28:45', 0, 1, NULL, '/order', 'Layout', NULL, NULL, 0, '订单管理', '订单管理', 'order', NULL, NULL, NULL, NULL, 90); +INSERT INTO `menu` VALUES (148, '2020-12-04 11:32:46', '2021-01-28 09:52:10', 147, 1, NULL, 'order', 'order/order', NULL, NULL, 0, 'order', '订单管理', NULL, NULL, NULL, NULL, NULL, 1); +INSERT INTO `menu` VALUES (152, '2020-12-16 10:16:39', '2021-01-28 10:13:32', 141, 1, NULL, 'list', 'product/list', NULL, NULL, 0, '商品列表', '商品列表', '', NULL, NULL, NULL, NULL, 5); +INSERT INTO `menu` VALUES (155, '2020-12-01 14:20:42', '2021-04-22 10:30:44', 0, 2, NULL, '/rbac', 'Layout', NULL, NULL, 0, '权限管理', '权限管理', 'el-icon-office-building', NULL, NULL, NULL, NULL, 99); +INSERT INTO `menu` VALUES (156, '2020-12-01 14:22:09', '2021-04-22 10:30:44', 155, 2, NULL, '/platform/sys_user', 'platform/sys-user', NULL, NULL, 0, '用户管理', '用户管理', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (157, '2020-12-03 16:49:48', '2021-04-22 10:30:44', 155, 2, NULL, 'role', 'rbac/role', NULL, NULL, 0, '角色管理', '角色管理', NULL, NULL, NULL, NULL, NULL, 1); +INSERT INTO `menu` VALUES (158, '2020-12-03 16:49:48', '2021-04-22 10:30:44', 155, 2, NULL, 'menu', 'rbac/menu', NULL, NULL, 0, '平台菜单管理', '平台菜单管理', NULL, NULL, NULL, NULL, NULL, 2); +INSERT INTO `menu` VALUES (159, '2020-12-03 16:49:48', '2021-04-22 10:30:44', 155, 2, NULL, 'menu_permission', 'rbac/menu-permission', NULL, NULL, 0, '菜单资源', '菜单资源', NULL, NULL, NULL, NULL, NULL, 3); +INSERT INTO `menu` VALUES (160, '2020-12-03 17:54:02', '2021-04-22 10:30:44', 164, 2, NULL, 'attr', 'product/attr', NULL, NULL, 0, '属性管理', '属性管理', NULL, NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (161, '2020-12-03 17:54:02', '2021-04-22 10:30:44', 164, 2, NULL, 'brand', 'product/brand', NULL, NULL, 0, '品牌管理', '品牌管理', NULL, NULL, NULL, NULL, NULL, 1); +INSERT INTO `menu` VALUES (162, '2020-12-03 17:54:02', '2021-04-22 10:30:44', 164, 2, NULL, 'category', 'product/category', NULL, NULL, 0, 'category', '分类管理', NULL, NULL, NULL, NULL, NULL, 2); +INSERT INTO `menu` VALUES (164, '2020-12-03 17:55:22', '2021-04-22 10:30:44', 0, 2, NULL, '/product', 'Layout', NULL, NULL, 0, 'product', '商品管理', 'el-icon-shopping-bag-1', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (175, '2020-12-16 10:16:39', '2021-04-22 10:30:44', 164, 2, NULL, 'list', 'product/list', NULL, NULL, 0, '商品管理', '商品管理', '', NULL, NULL, NULL, NULL, 5); +INSERT INTO `menu` VALUES (178, '2020-12-22 09:26:41', '2021-04-22 10:30:44', 155, 2, NULL, 'shop-menu', 'rbac/shop-menu', NULL, NULL, 0, '店铺菜单管理', '店铺菜单管理', '', NULL, NULL, NULL, NULL, 2); +INSERT INTO `menu` VALUES (255, '2021-01-27 14:10:56', '2021-04-22 10:30:44', 0, 2, NULL, '/platform', 'Layout', NULL, NULL, 0, 'platform', '平台管理', 'tree', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (256, '2021-01-27 14:14:51', '2021-04-22 10:30:44', 255, 2, NULL, 'hot-search', 'platform/hot-search', NULL, NULL, 0, 'hot-search', '热搜管理', 'el-icon-search', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (257, '2021-01-28 09:48:39', '2021-04-22 10:30:44', 255, 2, NULL, 'index-img', 'platform/index-img', NULL, NULL, 0, '轮播图管理', '轮播图管理', 'el-icon-picture-outline', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (258, '2021-01-28 11:06:52', '2021-01-28 11:06:52', 145, 1, NULL, 'hot-search', 'multishop/hot-search', NULL, NULL, 0, 'hot-search', '热搜管理', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (302, '2021-05-13 19:02:02', '2021-05-13 19:02:20', 255, 2, NULL, 'shop-detail', 'platform/shop-detail', NULL, NULL, 1, '店铺详情', '店铺详情', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (306, '2021-05-14 17:32:04', '2021-05-14 17:34:08', 255, 2, NULL, 'shop-edit', 'platform/shop-edit', NULL, NULL, 1, '店铺编辑', '店铺编辑', '', NULL, NULL, NULL, NULL, 0); +INSERT INTO `menu` VALUES (327, '2021-06-30 20:22:45', '2021-06-30 20:25:49', 255, 2, NULL, 'shop-manage', 'platform/shop-manage', NULL, NULL, 0, '店铺管理', '店铺管理', 'el-icon-s-order', NULL, NULL, NULL, NULL, 3); + +-- ---------------------------- +-- Table structure for menu_permission +-- ---------------------------- +DROP TABLE IF EXISTS `menu_permission`; +CREATE TABLE `menu_permission` ( + `menu_permission_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '菜单资源用户id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `menu_id` bigint NOT NULL COMMENT '资源关联菜单', + `biz_type` tinyint NOT NULL COMMENT '业务类型 1 店铺菜单 2平台菜单', + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '权限对应的编码', + `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资源名称', + `uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '资源对应服务器路径', + `method` tinyint NOT NULL COMMENT '请求方法 1.GET 2.POST 3.PUT 4.DELETE', + PRIMARY KEY (`menu_permission_id`) USING BTREE, + UNIQUE INDEX `uk_permission`(`permission`, `biz_type`) USING BTREE, + INDEX `idx_menuid`(`menu_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 230 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '菜单资源'; + +-- ---------------------------- +-- Records of menu_permission +-- ---------------------------- + +-- ---------------------------- +-- Table structure for role +-- ---------------------------- +DROP TABLE IF EXISTS `role`; +CREATE TABLE `role` ( + `role_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '角色id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `role_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称', + `remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `create_user_id` bigint UNSIGNED NOT NULL COMMENT '创建者ID', + `biz_type` tinyint NULL DEFAULT NULL COMMENT '业务类型 1 店铺菜单 2平台菜单', + `tenant_id` bigint NULL DEFAULT NULL COMMENT '所属租户', + PRIMARY KEY (`role_id`) USING BTREE, + INDEX `idx_tenant_id`(`tenant_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 66 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色'; + +-- ---------------------------- +-- Records of role +-- ---------------------------- + +-- ---------------------------- +-- Table structure for role_menu +-- ---------------------------- +DROP TABLE IF EXISTS `role_menu`; +CREATE TABLE `role_menu` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '关联id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `role_id` bigint UNSIGNED NOT NULL COMMENT '角色ID', + `menu_id` bigint UNSIGNED NULL DEFAULT NULL COMMENT '菜单ID', + `menu_permission_id` bigint UNSIGNED NULL DEFAULT NULL COMMENT '菜单资源用户id', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_roleid_menu_permission_id`(`role_id`, `menu_id`, `menu_permission_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1924 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色与菜单对应关系'; + +-- ---------------------------- +-- Records of role_menu +-- ---------------------------- + + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 81 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of undo_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for user_role +-- ---------------------------- +DROP TABLE IF EXISTS `user_role`; +CREATE TABLE `user_role` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '关联id', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `user_id` bigint UNSIGNED NOT NULL COMMENT '用户ID', + `role_id` bigint UNSIGNED NOT NULL COMMENT '角色ID', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_userid_roleid`(`user_id`, `role_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 254 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户与角色对应关系'; + +-- ---------------------------- +-- Records of user_role +-- ---------------------------- + + + +create database IF NOT EXISTS `mall4cloud_seata` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_seata; + +-- ---------------------------- +-- Table structure for branch_table +-- ---------------------------- +DROP TABLE IF EXISTS `branch_table`; +CREATE TABLE `branch_table` ( + `branch_id` bigint NOT NULL, + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `transaction_id` bigint NULL DEFAULT NULL, + `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `status` tinyint NULL DEFAULT NULL, + `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime(6) NULL DEFAULT NULL, + `gmt_modified` datetime(6) NULL DEFAULT NULL, + PRIMARY KEY (`branch_id`) USING BTREE, + INDEX `idx_xid`(`xid`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Records of branch_table +-- ---------------------------- + +-- ---------------------------- +-- Table structure for global_table +-- ---------------------------- +DROP TABLE IF EXISTS `global_table`; +CREATE TABLE `global_table` ( + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `transaction_id` bigint NULL DEFAULT NULL, + `status` tinyint NOT NULL, + `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_service_group` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `timeout` int NULL DEFAULT NULL, + `begin_time` bigint NULL DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime NULL DEFAULT NULL, + `gmt_modified` datetime NULL DEFAULT NULL, + PRIMARY KEY (`xid`) USING BTREE, + INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE, + INDEX `idx_transaction_id`(`transaction_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Records of global_table +-- ---------------------------- + +-- ---------------------------- +-- Table structure for lock_table +-- ---------------------------- +DROP TABLE IF EXISTS `lock_table`; +CREATE TABLE `lock_table` ( + `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_id` bigint NULL DEFAULT NULL, + `branch_id` bigint NOT NULL, + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime NULL DEFAULT NULL, + `gmt_modified` datetime NULL DEFAULT NULL, + PRIMARY KEY (`row_key`) USING BTREE, + INDEX `idx_branch_id`(`branch_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Records of lock_table +-- ---------------------------- + + + +create database IF NOT EXISTS `mall4cloud_user` default character set utf8 collate utf8_general_ci; + +USE mall4cloud_user; + + +-- ---------------------------- +-- Table structure for area +-- ---------------------------- +DROP TABLE IF EXISTS `area`; +CREATE TABLE `area` ( + `area_id` bigint NOT NULL AUTO_INCREMENT, + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `area_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '地址', + `parent_id` bigint NOT NULL COMMENT '上级地址', + `level` int NOT NULL COMMENT '等级(从1开始)', + PRIMARY KEY (`area_id`) USING BTREE, + INDEX `parent_id`(`parent_id`) USING BTREE COMMENT '上级id' +) ENGINE = InnoDB AUTO_INCREMENT = 659041 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '省市区地区信息'; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `branch_id` bigint NOT NULL, + `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 91 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `user_id` bigint NOT NULL COMMENT 'ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + `nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户昵称', + `pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '头像图片路径', + `status` int NOT NULL DEFAULT 1 COMMENT '状态 1 正常 0 无效', + PRIMARY KEY (`user_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表'; + +-- ---------------------------- +-- Table structure for user_addr +-- ---------------------------- +DROP TABLE IF EXISTS `user_addr`; +CREATE TABLE `user_addr` ( + `addr_id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `user_id` bigint NOT NULL COMMENT '用户ID', + `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机', + `is_default` tinyint NOT NULL COMMENT '是否默认地址 1是', + `consignee` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收货人', + `province_id` bigint NULL DEFAULT NULL COMMENT '省ID', + `province` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '省', + `city_id` bigint NULL DEFAULT NULL COMMENT '城市ID', + `city` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '城市', + `area_id` bigint NULL DEFAULT NULL COMMENT '区ID', + `area` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '区', + `post_code` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮编', + `addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '地址', + `lng` decimal(12, 6) NULL DEFAULT NULL COMMENT '经度', + `lat` decimal(12, 6) NULL DEFAULT NULL COMMENT '纬度', + PRIMARY KEY (`addr_id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 203 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户地址'; diff --git a/doc/img/readme/image-20210705143529597.png b/doc/img/readme/image-20210705143529597.png new file mode 100644 index 00000000..27447030 Binary files /dev/null and b/doc/img/readme/image-20210705143529597.png differ diff --git a/doc/img/readme/image-20210705151729559.png b/doc/img/readme/image-20210705151729559.png new file mode 100644 index 00000000..f37347c2 Binary files /dev/null and b/doc/img/readme/image-20210705151729559.png differ diff --git a/doc/img/readme/image-20210705151847270.png b/doc/img/readme/image-20210705151847270.png new file mode 100644 index 00000000..6f105bf5 Binary files /dev/null and b/doc/img/readme/image-20210705151847270.png differ diff --git a/doc/img/readme/image-20210705152010036.png b/doc/img/readme/image-20210705152010036.png new file mode 100644 index 00000000..c7821ec5 Binary files /dev/null and b/doc/img/readme/image-20210705152010036.png differ diff --git a/doc/img/readme/image-20210705152109738.png b/doc/img/readme/image-20210705152109738.png new file mode 100644 index 00000000..ae67289f Binary files /dev/null and b/doc/img/readme/image-20210705152109738.png differ diff --git a/doc/img/readme/uniapp-1625469707350.png b/doc/img/readme/uniapp-1625469707350.png new file mode 100644 index 00000000..13ce0410 Binary files /dev/null and b/doc/img/readme/uniapp-1625469707350.png differ diff --git "a/doc/img/readme/\345\260\217\347\250\213\345\272\217-1625472143277.png" "b/doc/img/readme/\345\260\217\347\250\213\345\272\217-1625472143277.png" new file mode 100644 index 00000000..8d496b28 Binary files /dev/null and "b/doc/img/readme/\345\260\217\347\250\213\345\272\217-1625472143277.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-1.png" "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-1.png" new file mode 100644 index 00000000..1aa1d904 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-1.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..9dbbd627 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/ideavm\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-1.png" "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-1.png" new file mode 100644 index 00000000..82be4184 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-1.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..360e2193 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/idea\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/\345\277\205\351\241\273\345\220\257\345\212\250\347\232\204\346\234\215\345\212\241.png" "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/\345\277\205\351\241\273\345\220\257\345\212\250\347\232\204\346\234\215\345\212\241.png" new file mode 100644 index 00000000..d4d04462 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\346\226\207\346\241\243/\345\277\205\351\241\273\345\220\257\345\212\250\347\232\204\346\234\215\345\212\241.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/SHOW MASTER STATUS.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/SHOW MASTER STATUS.png" new file mode 100644 index 00000000..71298fe0 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/SHOW MASTER STATUS.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..701f5bf6 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256.png" new file mode 100644 index 00000000..d9bf56f3 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\234\250mysql\344\270\255\347\232\204\351\205\215\347\275\256.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\256\211\350\243\205\346\210\220\345\212\237.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\256\211\350\243\205\346\210\220\345\212\237.png" new file mode 100644 index 00000000..c40ce743 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/canal\345\256\211\350\243\205\346\210\220\345\212\237.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-1.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-1.png" new file mode 100644 index 00000000..5276d0d0 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-1.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-2.png" new file mode 100644 index 00000000..6b174053 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/es\351\205\215\347\275\256\347\264\242\345\274\225-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/linxu\346\223\215\344\275\234.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/linxu\346\223\215\344\275\234.png" new file mode 100644 index 00000000..48aef290 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/linxu\346\223\215\344\275\234.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\345\210\233\345\273\272\346\241\266.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\345\210\233\345\273\272\346\241\266.png" new file mode 100644 index 00000000..0fa4b76e Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\345\210\233\345\273\272\346\241\266.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..5c6b4ec1 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256.png" new file mode 100644 index 00000000..6e45c8c3 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/minio\351\205\215\347\275\256.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/mysql\347\231\273\345\275\225.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/mysql\347\231\273\345\275\225.png" new file mode 100644 index 00000000..939e3e08 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/mysql\347\231\273\345\275\225.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\345\256\230\346\226\271\346\226\207\346\241\243.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\345\256\230\346\226\271\346\226\207\346\241\243.png" new file mode 100644 index 00000000..f4b6e37c Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\345\256\230\346\226\271\346\226\207\346\241\243.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\345\210\227\350\241\250.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\345\210\227\350\241\250.png" new file mode 100644 index 00000000..a9811557 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\345\210\227\350\241\250.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\347\274\226\350\276\221.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\347\274\226\350\276\221.png" new file mode 100644 index 00000000..45eb4575 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/nacos\351\205\215\347\275\256\347\274\226\350\276\221.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\345\256\211\350\243\205\346\210\220\345\212\237.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\345\256\211\350\243\205\346\210\220\345\212\237.png" new file mode 100644 index 00000000..cbcb00bb Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\345\256\211\350\243\205\346\210\220\345\212\237.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\346\223\215\344\275\234\350\277\207\347\250\2131.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\346\223\215\344\275\234\350\277\207\347\250\2131.png" new file mode 100644 index 00000000..bc054a29 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\346\223\215\344\275\234\350\277\207\347\250\2131.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-1.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-1.png" new file mode 100644 index 00000000..8425dbbd Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-1.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..a26f1eaf Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-3.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-3.png" new file mode 100644 index 00000000..d93db434 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\347\232\204broker\351\205\215\347\275\256-3.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..924b2b3f Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/rocketmq\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256-2.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256-2.png" new file mode 100644 index 00000000..35f6bcc5 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256-2.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256.png" new file mode 100644 index 00000000..cdd2b3a7 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\344\277\256\346\224\271\351\205\215\347\275\256.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\345\221\275\345\220\215\347\251\272\351\227\264.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\345\221\275\345\220\215\347\251\272\351\227\264.png" new file mode 100644 index 00000000..d1eeb48e Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\345\221\275\345\220\215\347\251\272\351\227\264.png" differ diff --git "a/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\346\234\215\345\212\241\345\210\227\350\241\250.png" "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\346\234\215\345\212\241\345\210\227\350\241\250.png" new file mode 100644 index 00000000..3e013898 Binary files /dev/null and "b/doc/img/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272\346\226\207\346\241\243/seata\346\234\215\345\212\241\345\210\227\350\241\250.png" differ diff --git "a/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\212\266\346\200\201\350\275\254\346\215\242.png" "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\212\266\346\200\201\350\275\254\346\215\242.png" new file mode 100644 index 00000000..de555a2a Binary files /dev/null and "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\212\266\346\200\201\350\275\254\346\215\242.png" differ diff --git "a/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\225\214\351\235\242.png" "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\225\214\351\235\242.png" new file mode 100644 index 00000000..f37347c2 Binary files /dev/null and "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\347\225\214\351\235\242.png" differ diff --git "a/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.png" "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.png" new file mode 100644 index 00000000..9df116dc Binary files /dev/null and "b/doc/img/\350\241\250\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.png" differ diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/Swagger\346\226\207\346\241\243.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/Swagger\346\226\207\346\241\243.md" new file mode 100644 index 00000000..ccaecaff --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/Swagger\346\226\207\346\241\243.md" @@ -0,0 +1,160 @@ +## Swagger文档 + +> 不管是knife4j还是swagger-bootstrap-ui,对外提供的地址依然是doc.html +> +> 本商城文档的访问路径:`域名+端口+/doc.html`,例如:http://localhost:8000/doc.html + +​ 注:8000为本商城网关的端口 + +>Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui。 + +[swagger官网](https://swagger.io/) + +[knife4j官方文档][https://xiaoym.gitee.io/knife4j/documentation/] + +### 添加依赖 + +``` +3.0.2 + + + com.github.xiaoymin + knife4j-micro-spring-boot-starter + ${knife4j.version} + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + +``` + +### 添加配置类并开启 + +在本商城的每个微服务模块的**config**文件夹中,有swagger相应的配置类,以rbac模块为例,配置如下: + +```java +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.rbac.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } +} +``` + +特别要注意的是,类中配置了api文件也就是controller包的路径,否则生成的文档无法成功扫描接口。 + +```java +.apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.rbac.controller")) +``` + +用`@Configuration`注解该类,让spring管理这个类 + +用`@Bean`标注方法等价于XML中配置bean + +用`@EnableSwagger2`标识要开启`Swagger2` + +用`@EnableKnife4j`标识要开启`Knife4j` + +### 接口使用 + +在配置好之后,我们就可以对swagger进行使用,比如在`SpuTagController`类中 + +```java +@RestController("appSpuTagController") +@RequestMapping("/ua/spu_tag") +@Api(tags = "商品分组表") +public class SpuTagController { + + @Autowired + private SpuTagService spuTagService; + + @GetMapping("/list") + @ApiOperation(value = "获取商品分组列表", notes = "获取商品分组列表") + @ApiImplicitParam(name = "shopId", value = "店铺id", dataType = "Long") + public ServerResponseEntity> list(@RequestParam(value = "shopId", defaultValue = "0") Long shopId) { + List categories = spuTagService.listByShopId(shopId); + return ServerResponseEntity.success(categories); + } +} +``` + +`@Api(tags="商品分组表")`定义标签分组接口,在这个类下定义的所有接口将位于这个标签之下 + +`@ApiOperation(value = "获取商品分组列表", notes = "获取商品分组列表")`定义具体的接口标题信息,notes可以为这个标签添加注释 + +`@ApiImplicitParam(name = "shopId", value = "店铺id", dataType = "Long")`对应的参数列表信息,后端返回给前端开发人员,这个接口需要传递什么参数及参数的说明 + +如有多个参数需要说明,可使用`@ApiImplicitParams()`下面可包含多个`@ApiImplicitParam()` + +例如: + +```java +@ApiImplicitParams({ + @ApiImplicitParam(name = "parentId", value = "分类ID", dataType = "Long"), + @ApiImplicitParam(name = "shopId", value = "店铺id", dataType = "Long") + }) +``` + +### 实体类 + +```java +public class ChangeShopCartItemDTO { + + @ApiModelProperty(value = "购物车ID", required = true) + private Long shopCartItemId; + + @NotNull(message = "商品ID不能为空") + @ApiModelProperty(value = "商品ID", required = true) + private Long spuId; + + @ApiModelProperty(value = "旧的skuId 如果传过来说明在变更sku", required = true) + private Long oldSkuId; + + @NotNull(message = "skuId不能为空") + @ApiModelProperty(value = "skuId", required = true) + private Long skuId; + + @ApiModelProperty(value = "店铺ID,前端不用传该字段") + private Long shopId; + + @NotNull(message = "商品个数不能为空") + @ApiModelProperty(value = "商品个数", required = true) + private Integer count; + + @ApiModelProperty(value = "商品是否勾选 true:勾选 ") + private Boolean isCheck; + + 一系列GET、SET、ToString方法。 +} +``` + +`@ApiModelProperty(value = "购物车ID", required = true)`利用这个注解可以告诉前端开发人员该字段代表的含义以及是否必传。 + +### 常用注解 + +| 注解 | 作用 | +| ------------------ | ------------------------------------ | +| @Api | 修饰整个类,描述Controller的作用 | +| @ApiOperation | 描述一个类的一个方法,或者说一个接口 | +| @ApiParam | 单个参数描述 | +| @ApiModel | 用对象来接收参数 | +| @ApiProperty | 用对象接收参数时,描述对象的一个字段 | +| @ApiResponse | HTTP响应其中1个描述 | +| @ApiResponses | HTTP响应整体描述 | +| @ApiIgnore | 使用该注解忽略这个API | +| @ApiError | 发生错误返回的信息 | +| @ApiImplicitParam | 一个请求参数 | +| @ApiImplicitParams | 多个请求参数 | diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/mall4cloud\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/mall4cloud\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272.md" new file mode 100644 index 00000000..d7fb22b2 --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/mall4cloud\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272.md" @@ -0,0 +1,53 @@ +## 1. 中间件安装 + +本项目是一个分布式的项目,依赖较多的中间件,所以要先将中间件搭建起来才能够启动后台项目。 + +中间件安装参考,可以看`开发环境搭建` 安装对应的中间件。 + +## 2. 导入项目 + +### 2.1 安装jdk + maven + git + +使用gitee下载开源项目。 + +使用IDEA打开项目。 + + +使用`ctrl + shift + r` 全局替换掉 `192.168.1.46` 为中间件服务器ip。 + +## 3. 设置idea内存 + +在idea启动所有的项目,是很吃力的事情。所以要修改下idea的配置,让其能有足够的内存启动项目。 + +### 3.1 减小jar启动占用内存 + +编辑虚拟机配置,将每个服务的内存改为512M,`-Xms512m -Xms512m -Xss256k`,如果机器实在内存不够,可以将512适当减少,但是减少到一定程度,如256m会造成java虚拟机进行频繁的垃圾回收,会更加卡,所以推荐512m。 + +![image-20210706101932640](../img/开发文档/idea配置-1.png) + +![image-20210706101954376](../img/开发文档/idea配置-2.png) + +### 3.2 增加idea可使用内存 + +编辑idea配置,增加内存,至少变为2G,根据需要,可以适当增大,以提高流畅度。 + +```vmoptions +-Xms512m +-Xmx2048m +-XX:ReservedCodeCacheSize=512m +-XX:+UseConcMarkSweepGC +-XX:SoftRefLRUPolicyMSPerMB=100 +``` + +![image-20210706102108314](../img/开发文档/ideavm配置-1.png) + +![image-20210706102135990](../img/开发文档/ideavm配置-2.png) + +配置完毕,重启idea,此时可以启动所有项目。 + +## 4. 启动项目 + +![image-20210706102545837](../img/开发文档/必须启动的服务.png) + +图中的红框是必须启动的项目,其他是按需启动,推荐全部启动。 + diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\216\210\346\235\203\346\240\241\351\252\214\346\265\201\347\250\213.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\216\210\346\235\203\346\240\241\351\252\214\346\265\201\347\250\213.md" new file mode 100644 index 00000000..cc499e0c --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\216\210\346\235\203\346\240\241\351\252\214\346\265\201\347\250\213.md" @@ -0,0 +1,70 @@ +## 授权校验流程 + +为了确保系统的安全与获取用户信息,一般情况下都是用token解决的,那么我们的系统,token是如何生成,又是如何校验的呢? + +### token的生成 + +在 `TokenStore` 有几个方法 + +```java +public class TokenStore { + /** + * 将用户的部分信息存储在token中,并返回token信息 + * @param userInfoInToken 用户在token中的信息 + * @return token信息 + */ + public TokenInfoBO storeAccessToken(UserInfoInTokenBO userInfoInToken) {} + /** + * 根据accessToken 获取用户信息 + * @param accessToken accessToken + * @param needDecrypt 是否需要解密 + * @return 用户信息 + */ + public ServerResponseEntity getUserInfoByAccessToken(String accessToken, boolean needDecrypt) {} + + /** + * 刷新token,并返回新的token + * @param refreshToken + * @return + */ + public ServerResponseEntity refreshToken(String refreshToken) {} +} +``` + +在`LoginController#login()` 方法中,登录完毕之后使用`storeAccessToken`将登录的用户信息保存在redis中 + +### token的校验 + +在我们的设计当中,会一个授权中心,专门用于用户的授权登录,并校验token。从而不需要在每个服务都去创建自身的授权方法。 + +我们用商品的服务`mall4cloud-product`来举例,我们可以发现在`pom.xml`中依赖了`mall4cloud-common-security`模块。 + +在模块中有个过滤器`AuthFilter`,里面有这么一段 + +```java +tokenFeignClient.checkToken(accessToken) +``` + +其中`tokenFeignClient` 是 `mall4cloud-api-auth` 模块的方法,该接口其实是`feign`的一个接口,而实现就是`mall4cloud-auth`进行实现。因为我们说过,我们的认证授权应该是一个统一的服务来的,而这个服务就是`mall4cloud-auth`服务。也就是说项目启动,几乎是必须启动该项目先的。 + +### 配置不需要授权就能访问的url + +其实并不是所有url都应该登录才能够被用户所访问到的,如浏览商品,搜索商品的时候,用户是不需要登录就能进行的操作,这个时候该怎么办呢?我们在回到我们的`AuthFilter`,里面有一段 + +```java +List excludePathPatterns = authConfigAdapter.excludePathPatterns(); +``` +这里边有个`authConfigAdapter`其实实现该类就能将对应的连接设置为可以访问,或不可以访问了。 + +### 用户角色权限 +在用户角色权限的模型中,一个用户的权限往往是需要登录才能知道的。也细化到每个url,每个方法某个用户是否能够访问。我们的系统有的需要rbac模型,有的不需要,所以我们提取了一个rbac模型的服务`mall4cloud-rbac`。我们回到`AuthFilter`,里面有一段 + +```java +// 省略... +authConfigAdapter.needRbac() && !checkRbac(userInfoInToken, req.getRequestURI(), req.getMethod()) +// 省略... +permissionFeignClient.checkPermission(checkPermissionDTO) +// 省略... +``` + +这里面的`permissionFeignClient` 其实也是一个feign服务,用于连接 `mall4cloud-rbac` 这个服务,进行rbac模型的校验。 diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\226\207\344\273\266\344\270\212\344\274\240.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\226\207\344\273\266\344\270\212\344\274\240.md" new file mode 100644 index 00000000..e27c248a --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\346\226\207\344\273\266\344\270\212\344\274\240.md" @@ -0,0 +1,61 @@ +## 文件上传 + +本系统支持minio文件上传 + +文件上传的配置,一般配置一遍就不需要配置了。 + +文件上传的流程分成两种: + +1. 将文件上传到服务器,再通过服务器上传到minio保存,再保存到本地。这种上传形式是需要消耗两倍的流量的 +2. 通过服务器返回一个token之类的密钥,然后前端有直接上传到minio的权限。这种上传形式只需要消耗单次上传的流量 + +我们采用的是第二种上传的形式,因为要前端去兼容minio的上传,所以不仅是后台,前端也是需要做文件上传的配置的 + +首先我们要修改后台的文件上传配置,后台的文件上传配置在 `nacos` 的配置中心进行配置 + +登录 `nacos` ,进入配置管理 - 配置列表,根据生产环境or测试环境不同,选择不同的命名空间,如测试环境是`public` 的命名空间 + +根据打包的配置,找到`application-{环境}.yml`进行编辑 + +```yaml +biz: + oss: + # resources-url是带有bucket的 + resources-url: http://192.168.1.46:9000/mall4cloud + type: 1 + endpoint: http://192.168.1.46:9000 + bucket: mall4cloud + access-key-id: admin + access-key-secret: admin123456 +``` + +这里对这些变量进行下解释: + +- type: 文件上传类型 1.minio +- bucket: 文件上传归档的一个桶(当成是一个最大的文件夹就好) + - 对`minio`在中间件搭建的时候创建的桶,参考中间件一键安装,创建的bucket +- access-key-id: + - minio可以直接根据docker启动的命令获取账号密码,这里取的是`MINIO_ROOT_USER`,也就是登录的账号 +- access-key-secret: + - minio可以直接根据docker启动的命令获取账号密码,这里取的是`MINIO_ROOT_PASSWORD`,也就是登录的密码 +- endpoint: 文件上传的时候,需要上传的路径 + - minio就是minio的路径 +- resources-url: resources-url是带有bucket的 + - minio就是minio的路径 + bucket + +除了后台要修改图片上传的配置,前端也是需要修改文件上传配置的 + +1. `mall4cloud-admin`、`mall4cloud-platform` 对于这两个项目修改根目录下的`.env.{环境}`相关文件,如开发环境修改`.env.development`文件。 + +- VUE_APP_RESOURCES_URL: 对应上面后台配置的resources-url +- VUE_APP_RESOURCES_TYPE: 对应上面后台配置的type + +2. `mall4cloud-pc` 这个项目修改 `plugins/config.js` + +- resourcesUrl: 对应上面后台配置的resources-url +- resourcesActionType: 对应上面后台配置的type + +3. `mall4cloud-uniapp` 这个项目修改 `src/utils/config.js` + +- resourcesUrl: 对应上面后台配置的resources-url +- resourcesActionType: 对应上面后台配置的type diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\233\256\345\275\225\347\273\223\346\236\204.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\233\256\345\275\225\347\273\223\346\236\204.md" new file mode 100644 index 00000000..60d940cf --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\233\256\345\275\225\347\273\223\346\236\204.md" @@ -0,0 +1,26 @@ +# 目录结构 + +``` +mall4cloud +├── mall4cloud-api -- api接口,仅对内使用,一般用来放feign的接口,对内使用 +├ └── mall4cloud-api-auth -- 授权 feign接口(只要需要授权验证的微服务,就需要用到该接口) +├ └── mall4cloud-api-leaf -- 分布式id feign接口(需要生成分布式唯一id的,就需要用到该接口) +├ └── mall4cloud-api-rbac -- 用户角色权限 feign接口(如果一个服务,需要校验菜单权限,就需要用到该接口) +├── mall4cloud-auth -- 授权服务,用户登陆生成token并返回,token的校验等就是使用该服务的 +├── mall4cloud-biz -- 第三方业务服务,如minio文件上传等 +├── mall4cloud-common -- 一些公共业务 +├ └── mall4cloud-common-cache -- 缓存模块 +├ └── mall4cloud-common-core -- 一些常用核心代码模块 +├ └── mall4cloud-common-database -- 数据库模块 +├ └── mall4cloud-common-database -- 验证授权等安全模块 +├── mall4cloud-gateway -- 网关服务 +├── mall4cloud-leaf -- 分布式id服务(使用美团的leaf创建分布式id) +├── mall4cloud-multishop -- 商家服务 +├── mall4cloud-order -- 订单服务 +├── mall4cloud-payment -- 支付服务 +├── mall4cloud-platform -- 平台服务 +├── mall4cloud-product -- 商品服务 +├── mall4cloud-rbac -- 菜单服务 +├── mall4cloud-search -- 搜索服务(使用elasticsearch实现) +├── mall4cloud-user -- 用户服务 +``` diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\345\274\202\345\270\270\345\244\204\347\220\206.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\345\274\202\345\270\270\345\244\204\347\220\206.md" new file mode 100644 index 00000000..001ac9c2 --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\345\274\202\345\270\270\345\244\204\347\220\206.md" @@ -0,0 +1,215 @@ +## 统一异常处理 + +### 后端异常处理 + +在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`mall4cloudException` 与异常管理类 `DefaultExceptionHandlerConfig`。 + +我们先来看下 `mall4cloudException`的代码 + +```java +public class mall4cloudException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private Object object; + + /** + * 响应状态码枚举 + */ + private ResponseEnum responseEnum; + + public mall4cloudException(String msg) { + super(msg); + } + + public mall4cloudException(String msg, Object object) { + super(msg); + this.object = object; + } + + public mall4cloudException(String msg, Throwable cause) { + super(msg, cause); + } + + + public mall4cloudException(ResponseEnum responseEnum) { + super(responseEnum.getMsg()); + this.responseEnum = responseEnum; + } + + public mall4cloudException(ResponseEnum responseEnum,Object object) { + super(responseEnum.getMsg()); + this.responseEnum = responseEnum; + this.object = object; + } + + + public Object getObject() { + return object; + } + + public ResponseEnum getResponseEnum() { + return responseEnum; + } +} +``` + +`ResponseEnum`为我们自定义的返回状态码的枚举类,定义为一个枚举类,更直观处理异常返回的状态码及异常内容,以后每增加一种异常情况,只需增加一个枚举实例即可,不用每一种异常都定义一个异常类。 + +```java +public enum ResponseEnum { + + /** + * ok + */ + OK("00000", "ok"), + + /** + * 用于直接显示提示用户的错误,内容由输入内容决定 + */ + SHOW_FAIL("A00001", ""), + + /** + * 方法参数没有校验,内容由输入内容决定 + */ + METHOD_ARGUMENT_NOT_VALID("A00002", ""), + + /** + * 无法读取获取请求参数 + */ + HTTP_MESSAGE_NOT_READABLE("A00003", "请求参数格式有误"), + + /** + * 未授权 + */ + UNAUTHORIZED("A00004", "Unauthorized"), + + /** + * 服务器出了点小差 + */ + EXCEPTION("A00005", "服务器出了点小差"); + + private final String code; + + private final String msg; + + public String value() { + return code; + } + + public String getMsg() { + return msg; + } + + ResponseEnum(String code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String toString() { + return "ResponseEnum{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + "} " + super.toString(); + } + +} +``` + +再来看看 `DefaultExceptionHandlerConfig`类 + +```java +@RestController +@RestControllerAdvice +public class DefaultExceptionHandlerConfig { + + private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionHandlerConfig.class); + + @ExceptionHandler(mall4cloudException.class) + public ResponseEntity> mall4cloudExceptionHandler(mall4cloudException e) { + logger.error("mall4cloudExceptionHandler", e); + + ResponseEnum responseEnum = e.getResponseEnum(); + // 失败返回失败消息 + 状态码 + if (responseEnum != null) { + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(responseEnum, e.getObject())); + } + // 失败返回消息 状态码固定为直接显示消息的状态码 + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.showFailMsg(e.getMessage())); + } +} +``` + +--- + +### 前端异常处理 + +前端请求与相应做了封装,请求响应的内容会被拦截器所拦截,当后台返回给前台特定的状态码,前台将显示不同报错信息。请求响应非常常见,我们查看在`src\utils\request.js`里面的其中一段代码 + +```javascript +service.interceptors.response.use( + response => { + const res = response.data + + if (res.code === '00000') { + return res.data + } + + // A00001 用于直接显示提示用户的错误,内容由输入内容决定 + // A00003 无法读取获取请求参数 + if (res.code === 'A00001' || res.code === 'A00003' || res.code === 'A00005') { + Message({ + message: res.msg || 'Error', + type: 'error', + duration: 1.5 * 1000 + }) + return Promise.reject(res) + } + + // A00002 方法参数没有校验,内容由输入内容决定 + if (res.code === 'A00002') { + if (res.data && res.data.length) { + res.data.forEach(errorMsg => { + Message({ + message: errorMsg || 'Error', + type: 'error', + duration: 1.5 * 1000 + }) + }) + } else { + Message({ + message: res.msg || 'Error', + type: 'error', + duration: 1.5 * 1000 + }) + } + return Promise.reject() + } + + // A00004 未授权 + if (res.code === 'A00004') { + // to re-login + MessageBox.confirm('您已注销,您可以取消停留在该页上,或重新登录', '确认注销', { + confirmButtonText: '重新登陆', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + store.dispatch('user/resetToken').then(() => { + location.reload() + }) + }) + return Promise.reject() + } + return Promise.reject(res) + }, + error => { + console.log('err' + error) // for debug + Message({ + message: error.message, + type: 'error', + duration: 1.5 * 1000 + }) + return Promise.reject(error) + } +) +``` + +这里将会统一拦截返回的状态码如`A00001`,进行错误提示。 diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\351\252\214\350\257\201.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\351\252\214\350\257\201.md" new file mode 100644 index 00000000..ef2acda2 --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\347\273\237\344\270\200\351\252\214\350\257\201.md" @@ -0,0 +1,113 @@ +## 统一验证 + +本商城使用spring提供的统一校验工具`spring-boot-starter-validation`对请求进行校验 + +### 导入依赖 + +```xml + + org.springframework.boot + spring-boot-starter-validation + +``` + +这里通过注解封装了几种常用的校验 + +- `@NotNull` 不能为null +- `@NotEmpty` 不能为null、空字符串、空集合 +- `@NotBlank` 不能为null、空字符串、纯空格的字符串 +- `@Min` 数字最小值不能小于x +- `@Max` 数字最大值不能大于x +- `@Email` 字符串为邮件格式 +- `@Max` 数字最大值不能大于x +- `@Size` 字符串长度最小为x、集合长度最小为x +- `@Pattern` 正则表达式 + +我们以`SpuDTO`为例,看看怎么使用 + +```java +public class SpuDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("spuId") + private Long spuId; + + @ApiModelProperty("品牌ID") + private Long brandId; + + @NotNull(message = "分类不能为空") + @ApiModelProperty("分类ID") + private Long categoryId; + + @NotNull(message = "店铺分类不能为空") + @ApiModelProperty("店铺分类ID") + private Long shopCategoryId; + + @NotNull(message = "商品名称不能为空") + @ApiModelProperty("spu名称") + private String name; + + /** 省略其余字段以及get、set、tostring方法*/ +} +``` + +我们在Controller层使用该bean,并使用`@Valid`注解,使校验的注解生效,如`SpuController` : + +```java +@RestController("platformSpuController") +@RequestMapping("/admin/spu") +@Api(tags = "admin-spu信息") +public class SpuController { + + @Autowired + private SpuService spuService; + + @PostMapping + @ApiOperation(value = "保存spu信息", notes = "保存spu信息") + public ServerResponseEntity save(@Valid @RequestBody SpuDTO spuDTO) { + checkSaveOrUpdateInfo(spuDTO); + spuService.save(spuDTO); + return ServerResponseEntity.success(); + } +} +``` + +并且在`DefaultExceptionHandlerConfig` 拦截由`@Valid` 触发的异常信息并返回: + +```java +@RestController +@RestControllerAdvice +public class DefaultExceptionHandlerConfig { + + @ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class }) + public ResponseEntity>> methodArgumentNotValidExceptionHandler(Exception e) { + logger.error("methodArgumentNotValidExceptionHandler", e); + List fieldErrors = null; + if (e instanceof MethodArgumentNotValidException) { + fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors(); + } + if (e instanceof BindException) { + fieldErrors = ((BindException) e).getBindingResult().getFieldErrors(); + } + if (fieldErrors == null) { + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID)); + } + List defaultMessages = new ArrayList<>(fieldErrors.size()); + for (FieldError fieldError : fieldErrors) { + defaultMessages.add(fieldError.getField() + ":" + fieldError.getDefaultMessage()); + } + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, defaultMessages)); + } + + @ExceptionHandler({ HttpMessageNotReadableException.class }) + public ResponseEntity>> methodArgumentNotValidExceptionHandler( + HttpMessageNotReadableException e) { + logger.error("methodArgumentNotValidExceptionHandler", e); + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.HTTP_MESSAGE_NOT_READABLE)); + } +} +``` + diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\350\241\250\346\240\274\345\210\206\351\241\265.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\350\241\250\346\240\274\345\210\206\351\241\265.md" new file mode 100644 index 00000000..6433c670 --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\350\241\250\346\240\274\345\210\206\351\241\265.md" @@ -0,0 +1,205 @@ +## 表格分页 + +本商城前端采用Element数据分页组件`Pagination`分页,后端采用`Mybatis`的`PageHelper`分页插件。 + +### 前端分页 + +前端采用`Pagination`分页,具体文档参考[Element UI][https://element.eleme.cn/#/zh-CN/component/pagination] + +本商城中,组件定义位置为:`src/components/Pagination/index.vue` + +```html + +``` + +该组件中定义了两个事件,其中`@size-change`为组件页数改变时会触发,而` @current-change`事件中,当前页数改变时即会触发该事件。 + +其他页面需要用到分页时,通过import导入该组件,并通过设置相关参数来使用,以下代码参考`src/views/order/order/index.vue` + +```html + +``` + +组件在被引用时,页面可以调用他的同名参数 `@pagination` + +```js +getPage() { + this.pageLoading = true + api.page({ ...this.pageQuery, ...this.searchParam }).then(pageVO => { + this.pageVO = pageVO + this.pageLoading = false + }) +} +``` + +### 后台分页 + +后端采用`Mybatis`的`PageHelper`分页插件,由`PageHelper-Spring-Boot-Starter`集成分页插件到Spring Boot来完成表格分页。 + +``` +使用pagehelper进行分页,该分页只能一对一。 +``` + +#### 导入依赖 + +```xml + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.4 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.3.0 + +``` + +#### 建立分页工具类 + +```java +public class PageUtil { + + /** + * 使用pagehelper进行分页,该分页只能一对一 + */ + public static PageVO doPage(PageDTO pageDTO, ISelect select) { + + PageSerializable simplePageInfo = PageHelper.startPage(pageDTO).doSelectPageSerializable(select); + + PageVO pageVO = new PageVO<>(); + pageVO.setList(simplePageInfo.getList()); + pageVO.setTotal(simplePageInfo.getTotal()); + pageVO.setPages(getPages(simplePageInfo.getTotal(), pageDTO.getPageSize())); + return pageVO; + } + + public static Integer getPages(long total, Integer pageSize) { + + if (total == -1) { + return 1; + } + if (pageSize > 0) { + return (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1)); + } + return 0; + } +} +``` + +#### 搜索 + +服务端`ShopUserController` + +```java +@RequestMapping(value = "/m/shop_user") +@RestController("multishopShopUserController") +@Api(tags = "店铺用户信息") +public class ShopUserController { + + @Autowired + private ShopUserService shopUserService; + + @GetMapping("/page") + @ApiOperation(value = "店铺用户列表", notes = "获取店铺用户列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, String nickName) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + PageVO shopUserPage = shopUserService.pageByShopId(pageDTO, userInfoInTokenBO.getTenantId(), nickName); + return ServerResponseEntity.success(shopUserPage); + } +} +``` + +```java +@Service +public class ShopUserServiceImpl implements ShopUserService { + + @Resource + private ShopUserMapper shopUserMapper; + + @Override + public PageVO pageByShopId(PageDTO pageDTO, Long shopId, String nickName) { + return PageUtil.doPage(pageDTO, () -> shopUserMapper.listByShopId(shopId, nickName)); + } +} +``` + +可见,传入的参数为`pageDTO`,该对象是根据**POJO**,即“Plain Old Java Object”->“简单java对象”而得。 + +> POJO的意义就在于它的简单而灵活性,因为它的简单和灵活,使得POJO能够任意扩展,从而胜任多个场合,也就让一个模型贯穿多个层成为现实。 + +| 名称 | 含义 | 说明 | +| :-----------------------: | :----------------------------------------: | :----------------------------------------------------------: | +| PO(Persistant Object) | 代表持久层对象的意思,对应数据库中表的字段 | 一个PO就是数据库中的一条记录 | +| BO(Business Object) | 把业务逻辑封装成一个对象 | 教育经历是一个PO,技术能力是一个PO,工作经历是一个PO,建立一个HR对象,也即BO去处理简历,每个BO均包含这些PO | +| VO(View Object) | 表现层对象 | 后台返回给前端的对象 | +| DTO(Data Transfer Object) | 数据传输对象 | 前端传给后台的对象 | + +`pageDTO`如下 + +```java +public class PageDTO implements IPage { + /** ...省略*/ + + /** + * 最大分页大小,如果分页大小大于500,则用500作为分页的大小。防止有人直接传入一个较大的数,导致服务器内存溢出宕机 + */ + public static final Integer MAX_PAGE_SIZE = 500; + + /** + * 当前页 + */ + @NotNull(message = "pageNum 不能为空") + @ApiModelProperty(value = "当前页", required = true) + private Integer pageNum; + + @NotNull(message = "pageSize 不能为空") + @ApiModelProperty(value = "每页大小", required = true) + private Integer pageSize; + + @ApiModelProperty(value = "排序字段数组,用逗号分割") + private String[] columns; + + @ApiModelProperty(value = "排序字段方式,用逗号分割,ASC正序,DESC倒序") + private String[] orders; + + /** ...省略*/ +} +``` + +返回给前端的参数`PageVO`如下: + +```java +public class PageVO { + + @ApiModelProperty("总页数") + private Integer pages; + + @ApiModelProperty("总条目数") + private Long total; + + @ApiModelProperty("结果集") + private List list; + + /** ...省略*/ +} +``` + +调用`PageUtil.doPage(pageDTO, () -> shopUserMapper.listByShopId(shopId, nickName))`方法,对返回的列表进行分页。 + diff --git "a/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\351\230\262\350\214\203xss\346\224\273\345\207\273.md" "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\351\230\262\350\214\203xss\346\224\273\345\207\273.md" new file mode 100644 index 00000000..5cd08c73 --- /dev/null +++ "b/doc/\345\237\272\346\234\254\345\274\200\345\217\221\346\226\207\346\241\243/\351\230\262\350\214\203xss\346\224\273\345\207\273.md" @@ -0,0 +1,126 @@ +## 防范`xss`攻击 + +为了防止用户传入一些假数据、假脚本对系统做出攻击,比较出名的就是`xss`攻击。故此,本商城在后台代码中采用过滤器来解决`xss`攻击。 + +在`mall4cloud`这个项目里面,使用了一个过滤器 `XssFilter` + +```java +public class XssFilter implements Filter { + + private static final Logger logger = LoggerFactory.getLogger(XssFilter.class); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + // replaceAll("[\r\n]" =》 Potential CRLF Injection for logs + logger.info("AuthFilter RequestURI :{}", req.getRequestURI().replaceAll("[\r\n]","")); + // xss 过滤 + chain.doFilter(new XssWrapper(req), resp); + } +} +``` + +主要是通过 `new XssWrapper(req)` 这个对象进行一系列的过滤,而 `XssWrapper` 是通过`Jsoup`进行用户输入的一系列过滤。毕竟专业的事情要交给专业的人来搞定。就此,我们通过简单的设置就完成了对**xss攻击**的防御。 + +```java +public class XssWrapper extends HttpServletRequestWrapper { + + /** + * Constructs a request object wrapping the given request. + * @param request The request to wrap + * @throws IllegalArgumentException if the request is null + */ + public XssWrapper(HttpServletRequest request) { + super(request); + } + + /** + * 对数组参数进行特殊字符过滤 + */ + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values == null) { + return null; + } + int count = values.length; + String[] encodedValues = new String[count]; + for (int i = 0; i < count; i++) { + encodedValues[i] = cleanXss(values[i]); + } + return encodedValues; + } + + /** + * 对参数中特殊字符进行过滤 + */ + @Override + public String getParameter(String name) { + String value = super.getParameter(name); + if (StrUtil.isBlank(value)) { + return value; + } + return cleanXss(value); + } + + /** + * 获取attribute,特殊字符过滤 + */ + @Override + public Object getAttribute(String name) { + Object value = super.getAttribute(name); + if (value instanceof String && StrUtil.isNotBlank((String) value)) { + return cleanXss((String) value); + } + return value; + } + + /** + * 对请求头部进行特殊字符过滤 + */ + @Override + public String getHeader(String name) { + String value = super.getHeader(name); + if (StrUtil.isBlank(value)) { + return value; + } + return cleanXss(value); + } + + private String cleanXss(String value) { + return XssUtil.clean(value); + } +} +``` + +这里面最主要的方法就是`XssUtil.clean(value)` -> `Jsoup.clean(content, "", WHITE_LIST, OUTPUT_SETTINGS)` 其中最重要的是有个白名单列表 `WHITE_LIST` ,该白名单列表里面是部分携带html的部分标签进入,从而防范xss攻击。 + +```java +new Whitelist().addTags( + "a", "b", "blockquote", "br", "caption", "cite", "code", "col", + "colgroup", "dd", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", + "i", "img", "li", "ol", "p", "pre", "q", "small", "span", "strike", "strong", + "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", + "ul") + .addAttributes("a", "href", "title") + .addAttributes("blockquote", "cite") + .addAttributes("col", "span", "width") + .addAttributes("colgroup", "span", "width") + .addAttributes("img", "align", "alt", "height", "src", "title", "width") + .addAttributes("ol", "start", "type") + .addAttributes("q", "cite") + .addAttributes("table", "summary", "width") + .addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width") + .addAttributes( + "th", "abbr", "axis", "colspan", "rowspan", "scope", + "width") + .addAttributes("ul", "type") + .addProtocols("a", "href", "ftp", "http", "https", "mailto") + .addProtocols("blockquote", "cite", "http", "https") + .addProtocols("cite", "cite", "http", "https") + .addProtocols("img", "src", "http", "https") + .addProtocols("q", "cite", "http", "https") +``` + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/1Centos\345\256\211\350\243\205Docker.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/1Centos\345\256\211\350\243\205Docker.md" new file mode 100644 index 00000000..14ff6efd --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/1Centos\345\256\211\350\243\205Docker.md" @@ -0,0 +1,115 @@ +## Centos安装 Docker + +从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。 + +Docker CE 即社区免费版,Docker EE 即企业版,强调安全,但需付费使用。 + +本文介绍 Docker CE 的安装使用。 + +移除旧的版本: + +```shell +$ sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-selinux \ + docker-engine-selinux \ + docker-engine +``` + +安装一些必要的系统工具: + +```shell +sudo yum install -y yum-utils device-mapper-persistent-data lvm2 +``` + +添加软件源信息: + +```shell +sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +``` + +更新 yum 缓存: + +```shell +# centos 7 +sudo yum makecache fast +# CentOS 8没有fast这个命令 +sudo yum makecache +``` + +安装 Docker-ce: + +```shell +sudo yum -y install docker-ce +``` + +查看已安装docker版本 + +```shell +docker version +``` + +启动 Docker 后台服务 + +```shell +sudo systemctl start docker +``` + +开机启动 + +```shell +sudo systemctl enable docker +``` + + +## 镜像加速 + +鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决。 + +可以使用阿里云的docker镜像地址:https://7qyk8phi.mirror.aliyuncs.com + +新版的 Docker 使用 `/etc/docker/daemon.json`(Linux,没有请新建)。 + +请在该配置文件中加入: + +(没有该文件的话,请先建一个) + +```javascript +{ + "registry-mirrors": ["https://7qyk8phi.mirror.aliyuncs.com"] +} +``` + +重启docker + +```shell +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +### 检查加速器是否生效 + +配置加速器之后,如果拉取镜像仍然十分缓慢,请手动检查加速器配置是否生效,在命令行执行 `docker info`,如果从结果中看到了如下内容,说明配置成功。 + +```shell +Registry Mirrors: + https://7qyk8phi.mirror.aliyuncs.com/ +``` + +### 下载docker-compose + +```shell +#运行此命令以下载 Docker Compose 的当前稳定版本 +sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +#对二进制文件应用可执行权限 +sudo chmod +x /usr/local/bin/docker-compose +#测试安装 +docker-compose --version +#若有docker-compose version 1.29.2, build 5becea4c,则安装成功 +``` + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/2Docker\346\220\255\345\273\272Mysql8.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/2Docker\346\220\255\345\273\272Mysql8.md" new file mode 100644 index 00000000..54032fe8 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/2Docker\346\220\255\345\273\272Mysql8.md" @@ -0,0 +1,97 @@ +### Docker搭建Mysql8 + +```shell +# 创建 sonarqube 工作目录,映射目录都放在这里 +mkdir -p /mydata/mysql + +# 编辑mysql配置 如下 +vi /mydata/mysql/my.cnf +``` + +将以下内容复制到配置文件中:[Linux中在控制台下Shift+Insert单击鼠标右键为粘贴键] + +```shell +[mysqld] +# 设置3306端口 +port=3306 +# 服务端使用的字符集默认为utf8mb4 +character-set-server=utf8mb4 +# 服务端使用的排序规则 +collation-server=utf8mb4_general_ci +# 创建新表时将使用的默认存储引擎 +default-storage-engine=INNODB +# 默认使用“mysql_native_password”插件认证 +# mysql 8.0 需要设置 mysql_native_password +default_authentication_plugin=mysql_native_password +# 关闭 only_full_group_by +sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION + +# binlog 日志存放路径 +log-bin=mysql-binlog +# 日志中记录每一行数据被修改的形式 +binlog-format=ROW +# 当前机器的服务 ID, 如果为集群时不能重复,不要和 canal 的 slaveId 重复 +server_id=1 + +[mysql] +# 设置mysql客户端默认字符集 +default-character-set=utf8mb4 +[client] +# 设置mysql客户端连接服务端时默认使用的端口 +port=3306 +``` + +Esc键后输入冒号,也即":"(不需要双引号),之后键入wq即保存退出 + +```shell +# 创建映射目录,查看日志 +mkdir /mydata/mysql/data && \ +mkdir /mydata/mysql/conf && \ +mkdir /mydata/mysql/logs && \ +mkdir /mydata/mysql/mysql-files + +# 设置为 777 权限 +chmod 777 /mydata/mysql/data && \ +chmod 777 /mydata/mysql/conf && \ +chmod 777 /mydata/mysql/logs && \ +chmod 777 /mydata/mysql/mysql-files + +# 下载mysql8 +docker pull mysql:8 + +# 启动容器 +docker run -p 3306:3306 --name mysql --restart=always \ + -v /mydata/mysql/conf:/etc/mysql \ + -v /mydata/mysql/logs:/var/log/mysql \ + -v /mydata/mysql/data:/var/lib/mysql \ + -v /mydata/mysql/mysql-files:/var/lib/mysql-files \ + -v /mydata/mysql/my.cnf:/etc/my.cnf \ + -e MYSQL_ROOT_PASSWORD=root \ + -e TZ=Asia/Shanghai \ + -d mysql:8 +``` + +#### 进入容器 + +```shell +docker exec -it mysql bash + +# 连接mysql,-p后输入密码 +mysql -uroot -p +root + +# 查看用户信息 +select user,host,authentication_string from mysql.user; + +# 设置权限(为root分配权限,以便可以远程连接) +grant all PRIVILEGES on *.* to root@'%' WITH GRANT OPTION; + +# 更新密码算法,便于使用sqlyog连接 +ALTER user 'root'@'%' IDENTIFIED BY 'root' PASSWORD EXPIRE NEVER; +ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root'; +FLUSH PRIVILEGES; +``` + +输入exit退出容器 + +使用数据库连接工具连接上mysql数据库后,执行`db/mall4cloud.sql`文件,确保之后服务运行正常。 \ No newline at end of file diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/3Docker\345\256\211\350\243\205minio.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/3Docker\345\256\211\350\243\205minio.md" new file mode 100644 index 00000000..e477b419 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/3Docker\345\256\211\350\243\205minio.md" @@ -0,0 +1,48 @@ +### Docker安装minio + +数据存储目录:/mydata/minio/data + +```shell +mkdir -p /mydata/minio/data + +# 设置为 666, 可读可写,但不能执行 +chmod 666 /mydata/minio/data +``` + +1、docker拉取最新镜像 + +```shell +docker pull minio/minio +``` + +2、运行minio + +```shell +docker run -p 9000:9000 \ + --name minio --restart=always \ + -v /mydata/minio/data:/data \ + -e "MINIO_ROOT_USER=AKIAIOSFODN342XAMPLE" \ + -e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MD81G/bPxRfiCYEXAMPLEKEY" \ + -d minio/minio server /data +``` + + +3、访问minio管理页面 + +```shell +http://localhost:9000/ + +账号:AKIAIOSFODN342XAMPLE + +密码:wJalrXUtnFEMI/K7MD81G/bPxRfiCYEXAMPLEKEY +``` + +4、访问minio管理页面,右下角有个加号,创建存储区域(create bucker) 名为`mall4cloud` + +![image-20210616090042502](../img/开发环境搭建文档/minio创建桶.png) + +若要前端可以读取图片,需要设置权限 + +![image-20210629112059876](../img/开发环境搭建文档/minio配置.png) + +![](../img/开发环境搭建文档/minio配置-2.png) \ No newline at end of file diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/4Docker\345\256\211\350\243\205RocketMQ.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/4Docker\345\256\211\350\243\205RocketMQ.md" new file mode 100644 index 00000000..962c43c8 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/4Docker\345\256\211\350\243\205RocketMQ.md" @@ -0,0 +1,127 @@ +### Docker安装rocketMQ + +1、docker拉取最新镜像 + +```shell +docker pull foxiswho/rocketmq:4.8.0 +docker pull styletang/rocketmq-console-ng +``` + +创建文件夹 + +```shell +mkdir -p /mydata/rocketmq/namesrv/logs && \ +mkdir -p /mydata/rocketmq/namesrv/store && \ +mkdir -p /mydata/rocketmq/broker/logs && \ +mkdir -p /mydata/rocketmq/broker/store && \ +mkdir -p /mydata/rocketmq/broker/conf + +# 设置为 777 权限,否则映射不生效 +chmod 777 /mydata/rocketmq/namesrv/logs && \ +chmod 777 /mydata/rocketmq/namesrv/store && \ +chmod 777 /mydata/rocketmq/broker/logs && \ +chmod 777 /mydata/rocketmq/broker/store && \ +chmod 777 /mydata/rocketmq/broker/conf +``` + +将文件夹中"**broker.conf**"复制到**/mydata/rocketmq/broker/conf/broker.conf** + +![image-20210616172837602](../img/开发环境搭建文档/rocketmq的broker配置-1.png) + +![image-20210616172819277](../img/开发环境搭建文档/rocketmq的broker配置-2.png) + +![image-20210616171820312](../img/开发环境搭建文档/rocketmq的broker配置-3.png) + +#### 注意: + +如果你的微服务或者项目在开发的时候没有放入docker中或者与rocketmq容器不能直接用IP访问,那么需要把**/mydata/rocketmq/broker/conf/broker.conf** 配置文件中的brokerIP1=192.168.1.21 这个启用,IP 地址填 你docker 所在宿主机的IP,否则报错。 + +![image-20210617175059541](../img/开发环境搭建文档/rocketmq配置-2.png) + +2、编写docker-compose + +```shell +mkdir ~/docker-build +#若显示文件已存在,则进行下一步cd +cd ~/docker-build +nano docker-compose.yml +``` + +```shell +version: '3.5' + +services: + rocketmq-namesrv: + image: foxiswho/rocketmq:4.8.0 + container_name: rocketmq-namesrv + ports: + - 9876:9876 + volumes: + - /mydata/rocketmq/namesrv/logs:/home/rocketmq/logs + - /mydata/rocketmq/namesrv/store:/home/rocketmq/store + environment: + JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128m" + command: ["sh","mqnamesrv"] + networks: + rocketmq: + aliases: + - rocketmq-namesrv + rocketmq-broker: + image: foxiswho/rocketmq:4.8.0 + container_name: rocketmq-broker + ports: + - 10909:10909 + - 10911:10911 + volumes: + - /mydata/rocketmq/broker/logs:/home/rocketmq/logs + - /mydata/rocketmq/broker/store:/home/rocketmq/store + - /mydata/rocketmq/broker/conf/broker.conf:/etc/rocketmq/broker.conf + environment: + JAVA_OPT_EXT: "-Duser.home=/home/rocketmq -Xms512M -Xmx512M -Xmn128m" + command: ["sh","mqbroker","-c","/etc/rocketmq/broker.conf","-n","rocketmq-namesrv:9876","autoCreateTopicEnable=true"] + depends_on: + - rocketmq-namesrv + networks: + rocketmq: + aliases: + - rocketmq-broker + + rocketmq-console: + image: styletang/rocketmq-console-ng + container_name: rocketmq-console + ports: + - 8180:8080 + environment: + JAVA_OPTS: "-Drocketmq.namesrv.addr=rocketmq-namesrv:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" + depends_on: + - rocketmq-namesrv + networks: + rocketmq: + aliases: + - rocketmq-console + +networks: + rocketmq: + name: rocketmq + driver: bridge +``` + +ctrl+X退出编辑,而后输入"Y",再按下回车即可保存编辑。 + +![image-20210616100121147](../img/开发环境搭建文档/rocketmq操作过程1.png) + +3.启动 + +```shell +docker-compose up -d +#如出现"-bash: docker-compose: command not found",请参考文件"源生Centos安装Docker"中的安装docker-compose再执行此命令 +``` + +4. 访问浏览器: + +```shell +localhost:8180 +``` + +![image-20210616103947309](../img/开发环境搭建文档/rocketmq安装成功.png) + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/5Docker\345\256\211\350\243\205Redis.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/5Docker\345\256\211\350\243\205Redis.md" new file mode 100644 index 00000000..f0d1c649 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/5Docker\345\256\211\350\243\205Redis.md" @@ -0,0 +1,15 @@ +### Docker安装Redis + +docker拉取最新镜像 + +```shell +docker pull redis +``` + +启动redis + +```shell +docker run --name redis --restart=always -p 6379:6379 \ +-d redis +``` + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/6Docker\345\256\211\350\243\205Nacos.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/6Docker\345\256\211\350\243\205Nacos.md" new file mode 100644 index 00000000..3497ba92 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/6Docker\345\256\211\350\243\205Nacos.md" @@ -0,0 +1,191 @@ +### Docker安装Nacos + +#### 环境准备 + +由于Nacos是配置中心,需要连接数据库,所以请先参考文件'**Docker安装Mysql**'安装mysql8版本后,再安装Nacos。 + +#### 步骤 + +```shell +#新建logs目录 +mkdir -p /mydata/nacos/logs/ +mkdir -p /mydata/nacos/init.d/ +``` + +docker拉取指定镜像版本 + +```shell +docker pull nacos/nacos-server:2.0.1 +``` + +安装mysql后,进入数据库执行sql脚本,文件'**mall4cloud_nacos.sql**'。 + +```shell +#修改配置文件 +vi /mydata/nacos/init.d/custom.properties +``` + +将以下内容复制到配置文件中,根据实际情况更改数据库的ip地址以及账号密码。 + +```shell +server.contextPath=/nacos +server.servlet.contextPath=/nacos +server.port=8848 +spring.datasource.platform=mysql +db.num=1 +db.url.0=jdbc:mysql://localhost:3306/mall4cloud_nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true +db.user=root +db.password=root +nacos.cmdb.dumpTaskInterval=3600 +nacos.cmdb.eventTaskInterval=10 +nacos.cmdb.labelTaskInterval=300 +nacos.cmdb.loadDataAtStart=false +management.metrics.export.elastic.enabled=false +management.metrics.export.influx.enabled=false +server.tomcat.accesslog.enabled=true +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i +nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/** +nacos.naming.distro.taskDispatchThreadCount=1 +nacos.naming.distro.taskDispatchPeriod=200 +nacos.naming.distro.batchSyncKeyCount=1000 +nacos.naming.distro.initDataRatio=0.9 +nacos.naming.distro.syncRetryDelay=5000 +nacos.naming.data.warmup=true +nacos.naming.expireInstance=true +``` + +修改完毕后,按ESC键后,键入":wq",wq:表示保存退出 + +启动容器,MYSQL_SERVICE_HOST修改为数据库ip地址 + +```shell +docker run \ +--name nacos -d \ +-p 8848:8848 \ +-p 9848:9848 \ +-p 9849:9849 \ +--privileged=true \ +--restart=always \ +-e JVM_XMS=256m \ +-e JVM_XMX=256m \ +-e MODE=standalone \ +-e PREFER_HOST_MODE=hostname \ +-e SPRING_DATASOURCE_PLATFORM=mysql \ +-e MYSQL_SERVICE_HOST=192.168.1.21 \ +-e MYSQL_SERVICE_DB_NAME=mall4cloud_nacos \ +-e MYSQL_SERVICE_USER=root \ +-e MYSQL_SERVICE_PASSWORD=root \ +-v /mydata/nacos/logs:/home/nacos/logs \ +-v /mydata/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties \ +nacos/nacos-server:2.0.1 +``` + +由于是docker部署,所以启动时需要配置9848、9849两个端口[参考官方文档https://nacos.io/zh-cn/docs/2.0.0-compatibility.html] + +![image-20210617145246516](../img/开发环境搭建文档/nacos官方文档.png) + +即可访问 + +http://localhost:8848/ + +用户名:nacos + +密码:nacos + +![image-20210615092901375](../img/开发环境搭建文档/nacos配置列表.png) + +修改public中各个配置的内容,点击"编辑",根据自己的ip地址填入信息,例如: + +![image-20210617092746781](../img/开发环境搭建文档/nacos配置编辑.png) + +修改该服务模块连接的数据库ip地址。 + +修改"application-dev.yml"文件内容: + +```shell +# 数据源 +spring: + #避免nacos取网卡出错 + cloud: + inetutils: + preferred-networks: 192.168.1 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + type: com.zaxxer.hikari.HikariDataSource + hikari: + minimum-idle: 0 + maximum-pool-size: 20 + idle-timeout: 25000 + auto-commit: true + connection-test-query: SELECT 1 + redis: + #填写配置redis的ip + host: 192.168.1.21 + # password: null + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + +#mybatis的相关配置 +mybatis: + #mapper配置文件 + mapper-locations: classpath:mapper/*Mapper.xml + type-aliases-package: com.mall4j.cloud.**.model + #开启驼峰命名 + configuration: + map-underscore-to-camel-case: true + +seata: + config: + type: nacos + nacos: + #填写nacos中seata的命名id + namespace: 4b70485d-72dd-44df-a76a-7a3f578a3001 + server-addr: ${spring.cloud.nacos.discovery.server-addr} + password: ${spring.cloud.nacos.discovery.username} + username: ${spring.cloud.nacos.discovery.password} + registry: + type: nacos + nacos: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + password: ${spring.cloud.nacos.discovery.username} + username: ${spring.cloud.nacos.discovery.password} + namespace: ${seata.config.nacos.namespace} + +logging: + level: + root: info + com: + mall4cloud: + shop: debug +# 分页合理化,当查询到页码大于最后一页的时候,返回最后一页的数据,防止vue在最后一页删除时,数据不对的问题 +pagehelper: + reasonable: true + +biz: + oss: + type: 1 + endpoint: http://192.168.1.21:9000 + bucket: mall4cloud + access-key-id: AKIAIOSFODN342XAMPLE + access-key-secret: wJalrXUtnFEMI/K7MD81G/bPxRfiCYEXAMPLEKEY + +feign: + client: + config: + default: + connectTimeout: 5000 + readTimeout: 5000 + loggerLevel: basic + inside: + key: mall4cloud-feign-inside-key + secret: mall4cloud-feign-inside-secret + # ip白名单,如果有需要的话,用小写逗号分割 + ips: + +rocketmq: + #填写配置rocketmq的地址,端口号默认9876 + name-server: 192.168.1.21:9876 + +``` + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/7Docker\345\256\211\350\243\205Seata.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/7Docker\345\256\211\350\243\205Seata.md" new file mode 100644 index 00000000..62a0f550 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/7Docker\345\256\211\350\243\205Seata.md" @@ -0,0 +1,66 @@ +### Docker安装Seata + +安装seata前,需要使用docker安装好nacos配置中心,参考文件"**Docker安装Nacos**"。 + +```shell +#docker拉取指定版本的seata +docker pull seataio/seata-server:1.4.2 +#启动 +docker run --name seata-server -p 8091:8091 -d seataio/seata-server:1.4.2 +#将配置文件复制到指定文件夹,方便修改配置 +docker cp seata-server:/seata-server /opt/seata-config/ +#停止seata运行 +docker stop seata-server +#删除 +docker rm seata-server +#修改配置文件registry +vi /opt/seata-config/resources/registry.conf +``` + +配置文件中**serverAddr修改为自己的ip地址**,namespace参考nacos中的命名空间,如下: + +![image-20210615153745469](../img/开发环境搭建文档/seata命名空间.png) + +```shell +registry { + type = "nacos" + nacos { + application = "seata-server" + serverAddr = "localhost:8848" + group = "SEATA_GROUP" + namespace = "4b70485d-72dd-44df-a76a-7a3f578a3001" + cluster = "default" + username = "nacos" + password = "nacos" + } +} +config { + type = "nacos" + nacos { + serverAddr = "localhost:8848" + namespace = "4b70485d-72dd-44df-a76a-7a3f578a3001" + group = "SEATA_GROUP" + username = "nacos" + password = "nacos" + } +} +``` + +启动seata。 + +```shell +docker run --name seata-server -d \ + -p 8091:8091 \ + -e SEATA_IP=192.168.1.21 \ + -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \ + -v /opt/seata-config/resources:/root/seata-config \ + seataio/seata-server:1.4.2 +``` + +nacos中出现以下服务即安装成功。![image-20210615154438981](../img/开发环境搭建文档/seata服务列表.png) + +**修改配置内容** + +![image-20210616173417485](../img/开发环境搭建文档/seata修改配置.png) + +![image-20210616173502463](../img/开发环境搭建文档/seata修改配置-2.png) \ No newline at end of file diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/8Docker\345\256\211\350\243\205ElasticSearch.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/8Docker\345\256\211\350\243\205ElasticSearch.md" new file mode 100644 index 00000000..8b1187d8 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/8Docker\345\256\211\350\243\205ElasticSearch.md" @@ -0,0 +1,360 @@ +### Docker安装ElasticSearch + +### 1、安装elasticsearch + +(1)下载elasticsearch和kibana + +```shell +docker pull elasticsearch:7.9.1 +docker pull kibana:7.9.1 +``` + +(2)配置 + +```shell +mkdir -p /mydata/elasticsearch/config +mkdir -p /mydata/elasticsearch/data +echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml +# 设置为 777 权限 +chmod -R 777 /mydata/elasticsearch/ +``` + +(3)启动Elastic search + +```shell +docker run --name elasticsearch --restart=always -p 9200:9200 -p 9300:9300 \ +-e "discovery.type=single-node" \ +-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \ +-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ +-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ +-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ +-d elasticsearch:7.9.1 +``` + +(4)启动kibana: + +```shell +docker run --name kibana --restart=always --link elasticsearch:elasticsearch -p 5601:5601 -d kibana:7.9.1 +``` + +### 2、安装IK + +IK项目地址:https://github.com/medcl/elasticsearch-analysis-ik + +首先需要说明的是,IK插件必须和 ElasticSearch 的版本一致,否则不兼容。 + +本系统采用的ElasticSearch版本为7.9.1 + +#### 安装 + +- 方法1:在线安装 + + ```shell + #进入容器 + docker exec -it elasticsearch /bin/bash + + #下载 + elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.9.1/elasticsearch-analysis-ik-7.9.1.zip + + #退出并重新启动ElasticSearch + exit + docker restart elasticsearch + ``` + +- 方法2:离线安装 + + 从 https://github.com/medcl/elasticsearch-analysis-ik/releases 下载压缩包,然后在ES的plugins目录创建analysis-ik子目录,把压缩包的内容复制到这个目录里面即可。最终plugins/analysis-ik/目录里面的内容: + + plugins/analysis-ik/ + commons-codec-1.9.jar + commons-logging-1.2.jar + elasticsearch-analysis-ik-6.2.4.jar + httpclient-4.5.2.jar + httpcore-4.4.4.jar + plugin-descriptor.properties + 然后重启 ElasticSearch。 + + 可参考https://www.cnblogs.com/szwdun/p/10664348.html + +### 3、新建索引 + +根据自己本地ip地址打开控制台,如下界面 + +http://localhost:5601/app/dev_tools#/console + +![image-20210621114010421](../img/开发环境搭建文档/es配置索引-1.png) + +需创建`order`、`product`两个索引 + +复制下述命令到左框,点击执行后即可 + +```json +PUT product +{ + "mappings" : { + "properties" : { + "attrs" : { + "type" : "nested", + "properties" : { + "attrId" : { + "type" : "long" + }, + "attrName" : { + "type" : "keyword" + }, + "attrValueId" : { + "type" : "long" + }, + "attrValueName" : { + "type" : "keyword" + } + } + }, + "tags" : { + "type" : "nested", + "properties" : { + "tagId" : { + "type" : "long" + }, + "seq" : { + "type" : "integer" + } + } + }, + "brandId" : { + "type" : "long" + }, + "brandImg" : { + "type" : "keyword" + }, + "brandName" : { + "type" : "keyword" + }, + "code" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "commentNum" : { + "type" : "integer" + }, + "createTime" : { + "type" : "date" + }, + "hasStock" : { + "type" : "boolean" + }, + "imgUrls" : { + "type" : "keyword", + "index" : false, + "doc_values" : false + }, + "mainImgUrl" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "marketPriceFee" : { + "type" : "long" + }, + "priceFee" : { + "type" : "long" + }, + "saleNum" : { + "type" : "integer" + }, + "sellingPoint" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "shopId" : { + "type" : "long" + }, + "shopImg" : { + "type" : "keyword", + "index" : false, + "doc_values" : false + }, + "shopName" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "shopType" : { + "type" : "integer" + }, + "shopPrimaryCategoryId" : { + "type" : "long" + }, + "shopPrimaryCategoryName" : { + "type" : "keyword" + }, + "shopSecondaryCategoryId" : { + "type" : "long" + }, + "shopSecondaryCategoryName" : { + "type" : "keyword" + }, + "primaryCategoryId" : { + "type" : "long" + }, + "primaryCategoryName" : { + "type" : "keyword" + }, + "secondaryCategoryId" : { + "type" : "long" + }, + "secondaryCategoryName" : { + "type" : "keyword" + }, + "categoryId" : { + "type" : "long" + }, + "categoryName" : { + "type" : "keyword" + }, + "spuId" : { + "type" : "long" + }, + "spuName" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "spuStatus" : { + "type" : "integer" + }, + "success" : { + "type" : "boolean" + } + } + } +} +``` + +![image-20210621131013166](../img/开发环境搭建文档/es配置索引-2.png) + +参照如上,创建`order`索引 + +```json +PUT order +{ + "mappings": { + "properties": { + "orderId": { + "type": "long" + }, + "createTime": { + "type": "date" + }, + "shopName": { + "type": "text", + "analyzer": "ik_max_word", + "search_analyzer": "ik_smart" + }, + "shopId": { + "type": "long" + }, + "userId": { + "type": "long" + }, + "consignee": { + "type": "text" + }, + "mobile": { + "type": "text" + }, + "status": { + "type": "integer" + }, + "deliveryType": { + "type": "integer" + }, + "total": { + "type": "long" + }, + "closeType": { + "type": "integer" + }, + "payTime": { + "type": "date" + }, + "deliveryTime": { + "type": "date" + }, + "finallyTime": { + "type": "date" + }, + "cancelTime": { + "type": "date" + }, + "isPayed": { + "type": "integer" + }, + "deleteStatus": { + "type": "integer" + }, + "orderItems": { + "type": "nested", + "properties": { + "pic": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "spuName": { + "type": "text", + "analyzer": "ik_max_word", + "search_analyzer": "ik_smart" + }, + "skuName": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "count": { + "type": "integer" + }, + "price": { + "type": "long" + }, + "skuId": { + "type": "long" + }, + "orderItemId": { + "type": "long" + }, + "spuId": { + "type": "long" + }, + "shopId": { + "type": "long" + }, + "userId": { + "type": "long" + }, + "deliveryType": { + "type": "integer" + }, + "shopCartTime": { + "type": "date" + }, + "spuTotalAmount": { + "type": "long" + } + } + } + } + } +} +``` + diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/9Docker\345\256\211\350\243\205canal.md" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/9Docker\345\256\211\350\243\205canal.md" new file mode 100644 index 00000000..299d8e21 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/9Docker\345\256\211\350\243\205canal.md" @@ -0,0 +1,363 @@ +### Docker安装canal + +安装canal需要有mysql,rabbitmq,若未安装,请参考文件”**Docker搭建Mysql8**“以及”**Docker安装RabbitMq**“。 + +安装前请确保数据库文件全部导入完毕,否则配置可能失效。 + +```shell +#进入容器 +docker exec -it mysql bash +# 连接mysql,-p后输入密码 +mysql -uroot -p +root +#查看是否开启日志 +mysql> show variables like '%log_bin%'; +``` + +![image-20210615155627621](../img/开发环境搭建文档/canal在mysql中的配置.png) + +#### 若无红框的内容,则参考以下步骤进行:[有则进行下一步] + +若出现 + +```shell +bash: xxx: command not found +``` + +需退出容器后才能执行以下步骤:[命令行输入exit] + +![image-20210615161942839](../img/开发环境搭建文档/linxu操作.png) + +1.1. 开启 MySQL 的 binlog 日志 + +修改 my.cnf, 添加配置项: + +```shell +vi /mydata/mysql/my.cnf +``` + +修改配置文件my的内容 + +![image-20210615160210060](../img/开发环境搭建文档/canal在mysql中的配置-2.png) + +```shell +[mysqld] +# binlog 日志存放路径 +log-bin=mysql-binlog +# 日志中记录每一行数据被修改的形式 +binlog-format=ROW +# 当前机器的服务 ID, 如果为集群时不能重复 +server_id=1 +``` + +重启 mysql 服务 + +```shell +docker restart mysql +``` + +查看配置变量是否生效: + +```shell +#进入容器 +docker exec -it mysql bash +#连接mysql,-p后输入密码 +mysql -uroot -p +root +#查看是否开启日志 +mysql> show variables like '%log_bin%'; ++---------------------------------+-----------------------------------+ +| Variable_name | Value | ++---------------------------------+-----------------------------------+ +| log_bin | ON | +| log_bin_basename | /var/lib/mysql/mysql-binlog | +| log_bin_index | /var/lib/mysql/mysql-binlog.index | +| log_bin_trust_function_creators | OFF | +| log_bin_use_v1_row_events | OFF | +| sql_log_bin | ON | ++---------------------------------+-----------------------------------+ +6 rows in set, 1 warning (0.00 sec) +``` + +### 若已开启日志,从此处继续: + +#### 配置 mysql 数据库的 canal 用户 + +mysql -uroot -p 登录 mysql + +![image-20210615160805552](../img/开发环境搭建文档/mysql登录.png) + +若是这种形式[mysql>]的命令行,直接执行以下命令,若否,则登录后执行,登录命令参照图片显示。 + +创建并授权用户 canal; + +```shell +CREATE USER canal IDENTIFIED BY 'canal'; + +GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; + +FLUSH PRIVILEGES; +``` + +### 安装单机 canal + +```shell +#新增文件夹存放配置文件 +mkdir /mydata/canal +#拉取指定版本镜像 +docker pull canal/canal-server:v1.1.4 +#启动canal +docker run -p 11111:11111 --name canal -id canal/canal-server:v1.1.4 +#复制配置文件至指定文件夹 +docker cp canal:/home/admin/canal-server/conf/canal.properties /mydata/canal +docker cp canal:/home/admin/canal-server/conf/example/instance.properties /mydata/canal +#停止 +docker stop canal +#删除 +docker rm canal +``` + + 配置文件 + +```shell +vi /mydata/canal/canal.properties +``` + +修改节点配置文件 canal.properties + +```shell +################################################# +######### common argument ############# +################################################# +# tcp bind ip +canal.ip = +# register ip to zookeeper +canal.register.ip = +canal.port = 11111 +canal.metrics.pull.port = 11112 +# canal instance user/passwd +# canal.user = canal +# canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458 + +# canal admin config +#canal.admin.manager = 127.0.0.1:8089 +canal.admin.port = 11110 +canal.admin.user = admin +canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441 + +canal.zkServers = +# flush data to zk +canal.zookeeper.flush.period = 1000 +canal.withoutNetty = false +# 此处修改为RocketMQ +canal.serverMode = RocketMQ +# flush meta cursor/parse position to file +canal.file.data.dir = ${canal.conf.dir} +canal.file.flush.period = 1000 +## memory store RingBuffer size, should be Math.pow(2,n) +canal.instance.memory.buffer.size = 16384 +## memory store RingBuffer used memory unit size , default 1kb +canal.instance.memory.buffer.memunit = 1024 +## meory store gets mode used MEMSIZE or ITEMSIZE +canal.instance.memory.batch.mode = MEMSIZE +canal.instance.memory.rawEntry = true + +## detecing config +canal.instance.detecting.enable = false +#canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now() +canal.instance.detecting.sql = select 1 +canal.instance.detecting.interval.time = 3 +canal.instance.detecting.retry.threshold = 3 +canal.instance.detecting.heartbeatHaEnable = false + +# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery +canal.instance.transaction.size = 1024 +# mysql fallback connected to new master should fallback times +canal.instance.fallbackIntervalInSeconds = 60 + +# network config +canal.instance.network.receiveBufferSize = 16384 +canal.instance.network.sendBufferSize = 16384 +canal.instance.network.soTimeout = 30 + +# binlog filter config +canal.instance.filter.druid.ddl = true +canal.instance.filter.query.dcl = false +canal.instance.filter.query.dml = false +canal.instance.filter.query.ddl = false +canal.instance.filter.table.error = false +canal.instance.filter.rows = false +canal.instance.filter.transaction.entry = false + +# binlog format/image check +canal.instance.binlog.format = ROW,STATEMENT,MIXED +canal.instance.binlog.image = FULL,MINIMAL,NOBLOB + +# binlog ddl isolation +canal.instance.get.ddl.isolation = false + +# parallel parser config +canal.instance.parser.parallel = true +## concurrent thread number, default 60% available processors, suggest not to exceed Runtime.getRuntime().availableProcessors() +#canal.instance.parser.parallelThreadSize = 16 +## disruptor ringbuffer size, must be power of 2 +canal.instance.parser.parallelBufferSize = 256 + +# table meta tsdb info +canal.instance.tsdb.enable = true +canal.instance.tsdb.dir = ${canal.file.data.dir:../conf}/${canal.instance.destination:} +canal.instance.tsdb.url = jdbc:h2:${canal.instance.tsdb.dir}/h2;CACHE_SIZE=1000;MODE=MYSQL; +canal.instance.tsdb.dbUsername = canal +canal.instance.tsdb.dbPassword = canal +# dump snapshot interval, default 24 hour +canal.instance.tsdb.snapshot.interval = 24 +# purge snapshot expire , default 360 hour(15 days) +canal.instance.tsdb.snapshot.expire = 360 + +# aliyun ak/sk , support rds/mq +canal.aliyun.accessKey = +canal.aliyun.secretKey = + +################################################# +######### destinations ############# +################################################# +canal.destinations = example +# conf root dir +canal.conf.dir = ../conf +# auto scan instance dir add/remove and start/stop instance +canal.auto.scan = true +canal.auto.scan.interval = 5 + +canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml +#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml + +canal.instance.global.mode = spring +canal.instance.global.lazy = false +canal.instance.global.manager.address = ${canal.admin.manager} +#canal.instance.global.spring.xml = classpath:spring/memory-instance.xml +canal.instance.global.spring.xml = classpath:spring/file-instance.xml +#canal.instance.global.spring.xml = classpath:spring/default-instance.xml + +################################################## +######### MQ ############# +################################################## +#此处修改为rocketmq的ip地址 +canal.mq.servers = localhost:9876 +canal.mq.retries = 3 +canal.mq.batchSize = 16384 +canal.mq.maxRequestSize = 1048576 +canal.mq.lingerMs = 100 +canal.mq.bufferMemory = 33554432 +canal.mq.canalBatchSize = 50 +canal.mq.canalGetTimeout = 100 +canal.mq.flatMessage = true +canal.mq.compressionType = none +canal.mq.acks = all +#canal.mq.properties. = +canal.mq.producerGroup = canal-topic +# Set this value to "cloud", if you want open message trace feature in aliyun. +canal.mq.accessChannel = local +# aliyun mq namespace +#canal.mq.namespace = + +################################################## +######### Kafka Kerberos Info ############# +################################################## +canal.mq.kafka.kerberos.enable = false +canal.mq.kafka.kerberos.krb5FilePath = "../conf/kerberos/krb5.conf" +canal.mq.kafka.kerberos.jaasFilePath = "../conf/kerberos/jaas.conf" +``` + +实例配置文件 instance.properties + +```shell +vi /mydata/canal/instance.properties +``` + +```shell +################################################# +## mysql serverId , v1.0.26+ will autoGen +# canal.instance.mysql.slaveId=0 + +canal.instance.gtidon=false + +# 填写数据库ip地址 +canal.instance.master.address=localhost:3306 +# 确保数据库文件全部执行完毕后,填写执行命令`SHOW MASTER STATUS`后的File内容 +canal.instance.master.journal.name=mysql-binlog.000030 +# 填写执行命令`SHOW MASTER STATUS`后的POSITION内容 +canal.instance.master.position=1175400 +canal.instance.master.timestamp= +canal.instance.master.gtid= + +# rds oss binlog +canal.instance.rds.accesskey= +canal.instance.rds.secretkey= +canal.instance.rds.instanceId= + +# table meta tsdb info +canal.instance.tsdb.enable=false +#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb +#canal.instance.tsdb.dbUsername=canal +#canal.instance.tsdb.dbPassword=canal + +#canal.instance.standby.address = +#canal.instance.standby.journal.name = +#canal.instance.standby.position = +#canal.instance.standby.timestamp = +#canal.instance.standby.gtid= + +# username/password +canal.instance.dbUsername=canal +canal.instance.dbPassword=canal +canal.instance.connectionCharset = UTF-8 +# enable druid Decrypt database password +canal.instance.enableDruid=false +#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ== + +# table regex +canal.instance.filter.regex=mall4cloud_product.spu:*,mall4cloud_product.category:*,mall4cloud_product.brand:*,mall4cloud_product.spu_tag_reference:*,mall4cloud_multishop.shop_detail:*,mall4cloud_product.spu_extension:*,mall4cloud_product.sku:*,mall4cloud_product.sku_stock:*,mall4cloud_order.order:* +# table black regex +canal.instance.filter.black.regex=mysql\\.slave_.* +# table field filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2) +#canal.instance.filter.field=test1.t_product:id/subject/keywords,test2.t_company:id/name/contact/ch +# table field black filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2) +#canal.instance.filter.black.field=test1.t_product:subject/product_image,test2.t_company:id/name/contact/ch + +# mq config +canal.mq.topic=canal-topic +# dynamic topic route by schema or table regex +#canal.mq.dynamicTopic=mytest1.user,mytest2\\..*,.*\\..* +canal.mq.partition=0 +# hash partition config +#canal.mq.partitionsNum=3 +#canal.mq.partitionHash=test.table:id^name,.*\\..* +################################################# +``` + +![image-20210615170124953](../img/开发环境搭建文档/SHOW MASTER STATUS.png) + + + +重启canal 服务 + +```shell +docker run \ +--name canal -d \ +-p 11111:11111 \ +-v /mydata/canal/conf/example:/home/admin/canal-server/conf/example \ +-v /mydata/canal/conf/canal.properties:/home/admin/canal-server/conf/canal.properties \ +-v /mydata/canal/logs:/home/admin/canal-server/logs \ +canal/canal-server:v1.1.4 +``` + +此时已经配置canal通过rocketmq监听mysql数据库,可以通过修改数据库文件来观察rocketmq是否有监听成功。 + +监听数据库范围在` /mydata/canal/instance.properties` + +```shell +#只有修改这些数据库才会监听 +canal.instance.filter.regex=mall4cloud_product.spu:*,mall4cloud_product.category:*,mall4cloud_product.brand:*,mall4cloud_product.spu_tag_reference:*,mall4cloud_multishop.shop_detail:*,mall4cloud_product.spu_extension:*,mall4cloud_product.sku:*,mall4cloud_product.sku_stock:* +``` + +![image-20210618094057889](../img/开发环境搭建文档/canal安装成功.png) \ No newline at end of file diff --git "a/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/broker.conf" "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/broker.conf" new file mode 100644 index 00000000..3db114f7 --- /dev/null +++ "b/doc/\345\274\200\345\217\221\347\216\257\345\242\203\346\220\255\345\273\272/broker.conf" @@ -0,0 +1,98 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +#所属集群名字 +brokerClusterName=DefaultCluster + +#broker名字,注意此处不同的配置文件填写的不一样,如果在broker-a.properties使用:broker-a, +#在broker-b.properties使用:broker-b +brokerName=broker-a + +#0 表示Master,>0 表示Slave +brokerId=0 + +#nameServer地址,分号分割 +#namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876 +namesrvAddr=rmqnamesrv:9876 + +#启动IP,如果 docker 报 com.alibaba.rocketmq.remoting.exception.RemotingConnectException: connect to <192.168.0.120:10909> failed +# 解决方式1 加上一句producer.setVipChannelEnabled(false);,解决方式2 brokerIP1 设置宿主机IP,不要使用docker 内部IP +#brokerIP1=192.168.0.253 + +#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数 +defaultTopicQueueNums=4 + +#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭 !!!这里仔细看是false,false,false +#原因下篇博客见~ 哈哈哈哈 +autoCreateTopicEnable=true + +#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭 +autoCreateSubscriptionGroup=true + +#Broker 对外服务的监听端口 +listenPort=10911 + +#删除文件时间点,默认凌晨4点 +deleteWhen=04 + +#文件保留时间,默认48小时 +fileReservedTime=120 + +#commitLog每个文件的大小默认1G +mapedFileSizeCommitLog=1073741824 + +#ConsumeQueue每个文件默认存30W条,根据业务情况调整 +mapedFileSizeConsumeQueue=300000 + +#destroyMapedFileIntervalForcibly=120000 +#redeleteHangedFileInterval=120000 +#检测物理文件磁盘空间 +diskMaxUsedSpaceRatio=88 +#存储路径 +#storePathRootDir=/home/ztztdata/rocketmq-all-4.1.0-incubating/store +#commitLog 存储路径 +#storePathCommitLog=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/commitlog +#消费队列存储 +#storePathConsumeQueue=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/consumequeue +#消息索引存储路径 +#storePathIndex=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/index +#checkpoint 文件存储路径 +#storeCheckpoint=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/checkpoint +#abort 文件存储路径 +#abortFile=/home/ztztdata/rocketmq-all-4.1.0-incubating/store/abort +#限制的消息大小 +maxMessageSize=65536 + +#flushCommitLogLeastPages=4 +#flushConsumeQueueLeastPages=2 +#flushCommitLogThoroughInterval=10000 +#flushConsumeQueueThoroughInterval=60000 + +#Broker 的角色 +#- ASYNC_MASTER 异步复制Master +#- SYNC_MASTER 同步双写Master +#- SLAVE +brokerRole=ASYNC_MASTER + +#刷盘方式 +#- ASYNC_FLUSH 异步刷盘 +#- SYNC_FLUSH 同步刷盘 +flushDiskType=ASYNC_FLUSH + +#发消息线程池数量 +#sendMessageThreadPoolNums=128 +#拉消息线程池数量 +#pullMessageThreadPoolNums=128 diff --git "a/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-1.md" "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-1.md" new file mode 100644 index 00000000..0cad8beb --- /dev/null +++ "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-1.md" @@ -0,0 +1,190 @@ +## 订单接口-1 + +用户下单时调用的订单接口有两个: + +1、确认订单[生成订单信息] + +2、提交订单[此时默认支付成功] + +### 1.确认订单 + +确认订单有以下几个步骤: + +1、判断是购物车还是直接购买 + +2、组装购物项信息 + +3、根据店铺计算金额 + +4、计算总额 + +--- + +用户通过点击"**立即购买**"或进入购物车选择"**结算**"进入到"**确认订单**"页面,具体接口:"`/a/order/confirm`" + +由于有两种方式进入到确认订单界面,但本商城设计为一个接口处理,统一下单: + +```java +public class OrderDTO { + + @ApiModelProperty(value = "立即购买时提交的商品项,如果该值为空,则说明是从购物车进入,如果该值不为空则说明为立即购买") + private ShopCartItemDTO shopCartItem; + + @NotNull(message = "配送类型不能为空") + @ApiModelProperty(value = "配送类型3:无需快递") + private Integer dvyType; +} +``` + +通过购物车适配器来**获取购物项组装信息** + +```java +public ServerResponseEntity> getShopCartItems(ShopCartItemDTO shopCartItemParam) { + ServerResponseEntity> shopCartItemResponse; + // 当立即购买时,没有提交的订单是没有购物车信息的 + if (shopCartItemParam != null) { + shopCartItemResponse = conversionShopCartItem(shopCartItemParam); + } + // 从购物车提交订单 + else { + shopCartItemResponse = shopCartFeignClient.getCheckedShopCartItems(); + } + if (!shopCartItemResponse.isSuccess()) { + return ServerResponseEntity.transform(shopCartItemResponse); + } + // 请选择您需要的商品加入购物车 + if (CollectionUtil.isEmpty(shopCartItemResponse.getData())) { + return ServerResponseEntity.fail(ResponseEnum.SHOP_CART_NOT_EXIST); + } + // 返回购物车选择的商品信息 + return shopCartItemResponse; +} +``` + +当购物项`shopCartItem`为空时,则说明是从购物车进入,此时调用获取**用户的购物车信息**的方法`shopCartFeignClient.getCheckedShopCartItems()`: + +```java +public ServerResponseEntity> getCheckedShopCartItems() { + //该方法从数据库查询购物车的商品 + List checkedShopCartItems = shopCartService.getCheckedShopCartItems(); + if (CollectionUtil.isNotEmpty(checkedShopCartItems)) { + for (ShopCartItemVO shopCartItem : checkedShopCartItems) { + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + } + } + return ServerResponseEntity.success(checkedShopCartItems); +} +``` + +而若`shopCartItem`不为空,则是直接购买进入该页面,调用`shopCartAdapter.conversionShopCartItem()`方法,组装购物信息: + +```java +public ServerResponseEntity> conversionShopCartItem(ShopCartItemDTO shopCartItemParam){ + ServerResponseEntity spuAndSkuResponse = spuFeignClient.getSpuAndSkuById(shopCartItemParam.getSpuId(),shopCartItemParam.getSkuId()); + if (!spuAndSkuResponse.isSuccess()) { + return ServerResponseEntity.transform(spuAndSkuResponse); + } + SkuVO sku = spuAndSkuResponse.getData().getSku(); + SpuVO spu = spuAndSkuResponse.getData().getSpu(); + // 拿到购物车的所有item + ShopCartItemVO shopCartItem = new ShopCartItemVO(); + shopCartItem.setCartItemId(0L); + shopCartItem.setSkuId(shopCartItemParam.getSkuId()); + shopCartItem.setCount(shopCartItemParam.getCount()); + shopCartItem.setSpuId(shopCartItemParam.getSpuId()); + shopCartItem.setSkuName(sku.getSkuName()); + shopCartItem.setSpuName(spu.getName()); + shopCartItem.setImgUrl(BooleanUtil.isTrue(spu.getHasSkuImg()) ? sku.getImgUrl() : spu.getMainImgUrl()); + shopCartItem.setSkuPriceFee(sku.getPriceFee()); + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + shopCartItem.setCreateTime(new Date()); + shopCartItem.setShopId(shopCartItemParam.getShopId()); + return ServerResponseEntity.success(Collections.singletonList(shopCartItem)); + } +``` + +两个路径的购物信息统一组装完毕后,不能在这一步删除购物车信息,万一用户点击返回后,发现购物车的商品虽然还没提交,但是已经消失,会造成信息差。 + +下一步,根据不同店铺来划分购物项,调用`shopCartAdapter.conversionShopCart()`: + +```java +public List conversionShopCart(List shopCartItems){ + + // 根据店铺ID划分item + Map> shopCartMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemVO::getShopId)); + + // 返回一个店铺的所有信息 + List shopCarts = Lists.newArrayList(); + for (Long shopId : shopCartMap.keySet()) { + // 构建每个店铺的购物车信息 + ShopCartVO shopCart = buildShopCart(shopId,shopCartMap.get(shopId)); + shopCart.setShopId(shopId); + shopCart.setShopCartItemVOS(shopCartMap.get(shopId)); + // 店铺信息 + ServerResponseEntity shopNameResponse = shopDetailFeignClient.getShopNameByShopId(shopId); + if (!shopNameResponse.isSuccess()) { + throw new mall4cloudException(shopNameResponse.getMsg()); + } + shopCart.setShopName(shopNameResponse.getData()); + shopCarts.add(shopCart); + } + return shopCarts; + } +``` + +在`buildShopCart(shopId,shopCartMap.get(shopId))`的时候,根据每个店铺来划分商品,再计算每个店铺商品的全部金额: + +```java +private ShopCartVO buildShopCart(Long shopId, List shopCartItems) { + ShopCartVO shopCart = new ShopCartVO(); + shopCart.setShopId(shopId); + long total = 0L; + int totalCount = 0; + for (ShopCartItemVO shopCartItem : shopCartItems) { + total += shopCartItem.getTotalAmount(); + totalCount += shopCartItem.getCount(); + } + shopCart.setTotal(total); + shopCart.setTotalCount(totalCount); + return shopCart; +} +``` + +此时根据店铺计算完金额后,在确认订单的时候,会重新计算一次总金额,返回给前端 + +```java +private void recalculateAmountWhenFinishingCalculateShop(ShopCartOrderMergerVO shopCartOrderMerger, List shopCarts) { + // 所有店铺的订单信息 + List shopCartOrders = new ArrayList<>(); + long total = 0; + int totalCount = 0; + // 所有店铺所有的商品item + for (ShopCartVO shopCart : shopCarts) { + // 每个店铺的订单信息 + ShopCartOrderVO shopCartOrder = new ShopCartOrderVO(); + shopCartOrder.setShopId(shopCart.getShopId()); + shopCartOrder.setShopName(shopCart.getShopName()); + total += shopCart.getTotal(); + totalCount += shopCart.getTotalCount(); + shopCartOrder.setTotal(shopCart.getTotal()); + shopCartOrder.setTotalCount(shopCart.getTotalCount()); + shopCartOrder.setShopCartItemVO(shopCart.getShopCartItemVOS()); + shopCartOrders.add(shopCartOrder); + } + shopCartOrderMerger.setTotal(total); + shopCartOrderMerger.setTotalCount(totalCount); + shopCartOrderMerger.setShopCartOrders(shopCartOrders); + } +``` + +最后,使用工具类对重复提交的订单做判断,并且将结果存入缓存中,提交订单时可取出使用: + +```java +// 防止重复提交 +RedisUtil.STRING_REDIS_TEMPLATE.opsForValue().set(OrderCacheNames.ORDER_CONFIRM_UUID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); +// 保存订单计算结果缓存,省得重新计算并且用户确认的订单金额与提交的一致 +cacheManagerUtil.putCache(OrderCacheNames.ORDER_CONFIRM_KEY,String.valueOf(userId),shopCartOrderMerger); +``` + +提交订单的接口设计请看下节:**订单接口-2** + diff --git "a/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-2.md" "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-2.md" new file mode 100644 index 00000000..08a6b130 --- /dev/null +++ "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-2.md" @@ -0,0 +1,190 @@ +## 订单接口-2 + +上节说到确认订单,这节讨论**提交订单**的接口设计。 + +调用接口为:`/a/order/submit` + +```java + @PostMapping("/submit") + @ApiOperation(value = "提交订单,返回支付流水号", notes = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付") + public ServerResponseEntity> submitOrders() { + Long userId = AuthUserContext.get().getUserId(); + //确认订单时将shopCartOrderMerger放入缓存中,提交时使用userId将其从缓存中取出 + ShopCartOrderMergerVO mergerOrder = cacheManagerUtil.getCache(OrderCacheNames.ORDER_CONFIRM_KEY,String.valueOf(userId)); + // 看看订单有没有过期 + if (mergerOrder == null) { + return ServerResponseEntity.fail(ResponseEnum.ORDER_EXPIRED); + } + // 与确认订单相同,使用RedisUtil.cad来检测原子性,判断是否重复提交 + boolean cad = RedisUtil.cad(OrderCacheNames.ORDER_CONFIRM_UUID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); + if (!cad) { + return ServerResponseEntity.fail(ResponseEnum.REPEAT_ORDER); + } + List orderIds = orderService.submit(userId,mergerOrder); + return ServerResponseEntity.success(orderIds); + } +``` + +提交订单时,将订单信息存入缓存,用户在提交订单的时候将会判断缓存内订单过期没有过期且非重复提交后,调用`orderService.submit(userId,mergerOrder)`方法来提交订单。 + +```java +@Override +@Transactional(rollbackFor = Exception.class) +public List submit(Long userId, ShopCartOrderMergerVO mergerOrder) { + List orders = saveOrder(userId, mergerOrder); + // 省略部分见下文 +} +``` + +首先将订单存入数据库,调用`saveOrder`方法: + +```java +public List saveOrder(Long userId, ShopCartOrderMergerVO mergerOrder) { + OrderAddr orderAddr = mapperFacade.map(mergerOrder.getUserAddr(), OrderAddr.class); + // 地址信息 + if (Objects.isNull(orderAddr)) { + // 请填写收货地址 + throw new mall4cloudException("请填写收货地址"); + } + // 保存收货地址 + orderAddrService.save(orderAddr); + // 订单商品参数 + List shopCartOrders = mergerOrder.getShopCartOrders(); + List orders = new ArrayList<>(); + List orderItems = new ArrayList<>(); + List shopCartItemIds = new ArrayList<>(); + if(CollectionUtil.isNotEmpty(shopCartOrders)) { + // 每个店铺生成一个订单 + for (ShopCartOrderVO shopCartOrderDto : shopCartOrders) { + Order order = getOrder(userId, mergerOrder.getDvyType(), shopCartOrderDto); + for (ShopCartItemVO shopCartItemVO : shopCartOrderDto.getShopCartItemVO()) { + OrderItem orderItem = getOrderItem(order, shopCartItemVO); + orderItems.add(orderItem); + shopCartItemIds.add(shopCartItemVO.getCartItemId()); + } + order.setOrderItems(orderItems); + order.setOrderAddrId(orderAddr.getOrderAddrId()); + orders.add(order); + } + } + orderMapper.saveBatch(orders); + orderItemService.saveBatch(orderItems); + // 清空购物车 + shopCartFeignClient.deleteItem(shopCartItemIds); + return orders; + } +``` + +每个店铺生成订单的时候,使用了`getOrder`方法,写入订单信息,将其状态设置为"**未付款**"。 + +```java +private Order getOrder(Long userId, Integer dvyType, ShopCartOrderVO shopCartOrderDto) { + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(Order.DISTRIBUTED_ID_KEY); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException("获取订单id失败"); + } + // 订单信息 + Order order = new Order(); + order.setOrderId(segmentIdResponse.getData()); + order.setShopId(shopCartOrderDto.getShopId()); + order.setShopName(shopCartOrderDto.getShopName()); + + // 用户id + order.setUserId(userId); + // 商品总额 + order.setTotal(shopCartOrderDto.getTotal()); + order.setStatus(OrderStatus.UNPAY.value()); + order.setIsPayed(0); + order.setDeleteStatus(0); + order.setAllCount(shopCartOrderDto.getTotalCount()); + order.setDeliveryType(DeliveryType.NOT_DELIVERY.value()); + return order; + } +``` + +完成后回到`orderService.submit(userId,mergerOrder)`方法 + +```java +List skuStockLocks = new ArrayList<>(); +for (Order order : orders) { + orderIds.add(order.getOrderId()); + List orderItems = order.getOrderItems(); + for (OrderItem orderItem : orderItems) { + skuStockLocks.add(new SkuStockLockDTO(orderItem.getSpuId(), orderItem.getSkuId(), orderItem.getOrderId(), orderItem.getCount())); + } +} +// 锁定库存 +ServerResponseEntity lockStockResponse = skuStockLockFeignClient.lock(skuStockLocks); +// 锁定不成,抛异常,让回滚订单 +if (!lockStockResponse.isSuccess()) { + throw new mall4cloudException(lockStockResponse.getMsg()); +} +``` + +提交订单的时候会把该订单的库存锁上`skuStockLockFeignClient.lock(skuStockLocks)`,多加一个库存锁定表,先减完spu和sku的库存后,再将锁库存的信息存入表中`sku_stock_lock`,倘若此时订单取消或者超过30分钟未支付,则通过`StockUnlockConsumer`监听来解锁库存。 + +```java +@Override +@Transactional(rollbackFor = Exception.class) +public ServerResponseEntity lock(List skuStockLocksParam) { + + List skuStockLocks = new ArrayList<>(); + for (SkuStockLockDTO skuStockLockDTO : skuStockLocksParam) { + //略... + // 减sku库存 + int skuStockUpdateIsSuccess = skuStockMapper.reduceStockByOrder(skuStockLockDTO.getSkuId(), skuStockLockDTO.getCount()); + if (skuStockUpdateIsSuccess < 1) { + throw new mall4cloudException(ResponseEnum.NOT_STOCK, skuStockLockDTO.getSkuId()); + } + // 减商品库存 + int spuStockUpdateIsSuccess = spuExtensionMapper.reduceStockByOrder(skuStockLockDTO.getSpuId(), skuStockLockDTO.getCount()); + if (spuStockUpdateIsSuccess < 1) { + throw new mall4cloudException(ResponseEnum.NOT_STOCK, skuStockLockDTO.getSkuId()); + } + } + // 保存库存锁定信息 + skuStockLockMapper.saveBatch(skuStockLocks); + List orderIds = skuStockLocksParam.stream().map(SkuStockLockDTO::getOrderId).collect(Collectors.toList()); + // 一个小时后解锁库存 + SendStatus sendStatus = stockMqTemplate.syncSend(RocketMqConstant.STOCK_UNLOCK_TOPIC, new GenericMessage<>(orderIds), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL + 1).getSendStatus(); + if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + return ServerResponseEntity.success(); +} +``` + +`stockMqTemplate.syncSend(...).getSendStatus();`此处发送库存解锁的消息,使用的`RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL`的参数 + +参考`RocketMqConstant`,16+1即第十七个,为一小时。 + +```java +public class RocketMqConstant { + + // 延迟消息 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h (1-18) + + /** + * 取消订单时间,实际上30分钟 + */ + public static final int CANCEL_ORDER_DELAY_LEVEL = 16; +} +``` + +锁定库存之后,发送消息,假如30分钟后尚未支付,则取消订单 + +```java +SendStatus sendStatus = orderCancelTemplate.syncSend(RocketMqConstant.ORDER_CANCEL_TOPIC, new GenericMessage<>(orderIds), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL).getSendStatus(); +if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new mall4cloudException(ResponseEnum.EXCEPTION); +} +``` + +最后返回的是订单ID列表,方便下一步进行支付 + +```java +return ServerResponseEntity.success(orderIds); +``` + +订单支付的接口设计请看下节:**订单接口-3** \ No newline at end of file diff --git "a/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-3.md" "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-3.md" new file mode 100644 index 00000000..4d655f36 --- /dev/null +++ "b/doc/\346\216\245\345\217\243\350\256\276\350\256\241/\350\256\242\345\215\225\346\216\245\345\217\243-3.md" @@ -0,0 +1,117 @@ +## 订单接口-3 + +确认订单-提交订单之后,就是支付了。 + +用户点击提交订单之后,选择确定付款,此时进入支付界面 + +前端调用接口为`/mall4cloud_payment/pay/order`,根据订单号进行支付,上一步提交订单返回的便是订单id列表 + +接口详情: + +```java +@PostMapping("/order") +@ApiOperation(value = "根据订单号进行支付", notes = "根据订单号进行支付") +public ServerResponseEntity pay(HttpServletRequest request, @Valid @RequestBody PayInfoDTO payParam) { + // 这里的地址是网关通过转发过来的时候,获取到当前服务器的地址,测试环境要用测试环境的uri + String gatewayUri = "http://192.168.1.17:8126/mall4cloud_payment"; + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + Long userId = userInfoInTokenBO.getUserId(); + PayInfoBO payInfo = payInfoService.pay(userId, payParam); + payInfo.setBizUserId(userInfoInTokenBO.getBizUserId()); + ... +} +``` + +这里关键的是调用了`payInfoService.pay(userId, payParam)` + +该方法由于涉及多个表库的增改,故此加了事务注解来保证一致性。 + +```java +@Override +@Transactional(rollbackFor = Exception.class) +public PayInfoBO pay(Long userId, PayInfoDTO payParam) { + // 支付单号 + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(PayInfo.DISTRIBUTED_ID_KEY); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + Long payId = segmentIdResponse.getData(); + List orderIds = payParam.getOrderIds(); + // 如果订单没有被取消的话,获取订单金额,否之会获取失败 + ServerResponseEntity ordersAmountAndIfNoCancelResponse = orderFeignClient.getOrdersAmountAndIfNoCancel(orderIds); + // 如果订单已经关闭了,此时不能够支付了 + if (!ordersAmountAndIfNoCancelResponse.isSuccess()) { + throw new mall4cloudException(ordersAmountAndIfNoCancelResponse.getMsg()); + } + //将数据存到数据库中 + OrderAmountVO orderAmount = ordersAmountAndIfNoCancelResponse.getData(); + PayInfo payInfo = new PayInfo(); + payInfo.setPayId(payId); + payInfo.setUserId(userId); + //支付的金额是从数据库查询的,并非前端传过来的值 + payInfo.setPayAmount(orderAmount.getPayAmount()); + payInfo.setPayStatus(PayStatus.UNPAY.value()); + payInfo.setSysType(AuthUserContext.get().getSysType()); + payInfo.setVersion(0); + // 保存多个支付订单号 + payInfo.setOrderIds(StrUtil.join(StrUtil.COMMA, orderIds)); + // 保存预支付信息 + payInfoMapper.save(payInfo); + PayInfoBO payInfoDto = new PayInfoBO(); + payInfoDto.setBody("商城订单"); + payInfoDto.setPayAmount(orderAmount.getPayAmount()); + payInfoDto.setPayId(payId); + //返回支付信息 + return payInfoDto; +} +``` + +`controller`方法剩下的便是支付回调了,前面的`gatewayUri`便有用了 + +```java +// 回调地址 + payInfo.setApiNoticeUrl(gatewayUri + "/notice/pay/order"); + payInfo.setReturnUrl(payParam.getReturnUrl()); + payNoticeController.submit(payInfo.getPayId()); + return ServerResponseEntity.success(payInfo.getPayId()); +``` + +执行完`pay`方法后,由于没有对接微信和支付宝接口,故直接调用submit中`payInfoService.paySuccess(payInfoResult,orderIdList);`使其支付成功 + +```java +@Override +@Transactional(rollbackFor = Exception.class) +public void paySuccess(PayInfoResultBO payInfoResult, List orderIds) { + // 标记为支付成功状态 + PayInfo payInfo = new PayInfo(); + payInfo.setPayId(payInfoResult.getPayId()); + payInfo.setBizPayNo(payInfoResult.getBizPayNo()); + payInfo.setCallbackContent(payInfoResult.getCallbackContent()); + payInfo.setCallbackTime(new Date()); + payInfo.setPayStatus(PayStatus.PAYED.value()); + payInfoMapper.update(payInfo); + // 发送消息,订单支付成功 + SendStatus sendStatus = orderNotifyTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_TOPIC, new GenericMessage<>(new PayNotifyBO(orderIds))).getSendStatus(); + if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,因为订单回调会有多次,几乎不可能每次都无法发送出去,发的出去无所谓因为接口是幂等的 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } +} +``` + +使用`rocketMq`来发送支付成功的消息,`OrderNotifyConsumer`进行订单回调,此时将订单改为已支付状态,并且发送消息通知库存可以扣减了 + +```java +@Override +public void onMessage(PayNotifyBO message) { + LOG.info("订单回调开始... message: " + Json.toJsonString(message)); + orderService.updateByToPaySuccess(message.getOrderIds()); + // 发送消息,订单支付成功,通知库存扣减 + SendStatus sendStockStatus = orderNotifyStockTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC, new GenericMessage<>(message)).getSendStatus(); + if (!Objects.equals(sendStockStatus,SendStatus.SEND_OK)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } +} +``` + +将支付单号返回给前端即可。 \ No newline at end of file diff --git "a/doc/\350\241\250\347\273\223\346\236\204\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.md" "b/doc/\350\241\250\347\273\223\346\236\204\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.md" new file mode 100644 index 00000000..8169505a --- /dev/null +++ "b/doc/\350\241\250\347\273\223\346\236\204\350\256\276\350\256\241/\350\256\242\345\215\225\350\241\250\347\273\223\346\236\204.md" @@ -0,0 +1,189 @@ +## 订单表结构 + +### 1 数据库结构 + +![image-20210623140003690](../img/表设计/订单表结构.png) + +一个订单表(order)中每一项对应多个`order_item`,通过`order_id`关联 + +- 一个order记录每个订单的状态、时间、总金额等 + +- 一个order有多个order_item,每个订单项记录每件商品的信息 + +#### 1.1 Order-订单 + +每一次下单即生成一条订单记录,其中有多件或一件商品。 + +```java + /** + * 订单ID + */ + private Long orderId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 店铺名称 + */ + private String shopName; + + /** + * 订单状态 1:待付款 2:待发货 3:待收货(已发货) 5:成功 6:失败 + */ + private Integer status; + + /** + * 订单关闭原因 1-超时未支付 4-买家取消 + */ + private Integer closeType; + + /** + * 总值 + */ + private Long total; + + /** + * 配送类型 :无需快递 + */ + private Integer deliveryType; + + /** + * 订单商品总数 + */ + private Integer allCount; + + /** + * 付款时间 + */ + private Date payTime; + + /** + * 发货时间 + */ + private Date deliveryTime; + + /** + * 完成时间 + */ + private Date finallyTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 是否已支付,1.已支付0.未支付 + */ + private Integer isPayed; + + /** + * 用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除 + */ + private Integer deleteStatus; +``` + +每个订单关联一个用户(`user_id`),一家店铺(`shop_id`) + +- `status`订单状态一共有六种:1:待付款 2:待发货 3:待收货(已发货) 5:成功 6:失败 + +**状态转换如下:** + +![image-20210623145605185](../img/表设计/订单状态转换.png) + +- `closeType`订单关闭原因有两种:1:超时未支付 2:买家取消 + +- `total`总值,指一个订单内所有商品的总金额,即这个订单关联的订单项中`spuTotalAmount`的总和 +- `deliveryType`配送类型,目前只有无需快递这一类型 +- `allCount`订单商品总数,即一个订单中包含的商品总数 + +#### 1.2 order_item-订单项 + +每个订单根据order_id关联订单项表,一个订单可以有多个订单项 + +```java + /** + * 订单项ID + */ + private Long orderItemId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 订单id + */ + private Long orderId; + + /** + * 产品ID + */ + private Long spuId; + + /** + * 产品SkuID + */ + private Long skuId; + + /** + * 用户Id + */ + private Long userId; + + /** + * 购物车产品个数 + */ + private Integer count; + + /** + * 产品名称 + */ + private String spuName; + + /** + * sku名称 + */ + private String skuName; + + /** + * 产品主图片路径 + */ + private String pic; + + /** + * 单个orderItem的配送类型 无需快递 + */ + private Integer deliveryType; + + /** + * 加入购物车时间 + */ + private Date shopCartTime; + + /** + * 产品价格 + */ + private Long price; + + /** + * 商品总金额 + */ + private Long spuTotalAmount; +``` + +- `count`购物车产品个数:添加到购物车的数量 +- `spuTotalAmount`商品总金额:为产品价格`price`与购物车产品个数`count`的乘积 + +### 2 界面 + +![image-20210705151729559](../img/表设计/订单界面.png) \ No newline at end of file diff --git a/es/order.md b/es/order.md new file mode 100644 index 00000000..681f5f4e --- /dev/null +++ b/es/order.md @@ -0,0 +1,119 @@ + +订单搜索 mapping + +PUT order + +```json +{ + "mappings": { + "properties": { + "orderId": { + "type": "long" + }, + "createTime": { + "type": "date" + }, + "shopName": { + "type": "text", + "analyzer": "ik_max_word", + "search_analyzer": "ik_smart" + }, + "shopId": { + "type": "long" + }, + "userId": { + "type": "long" + }, + "consignee": { + "type": "text" + }, + "mobile": { + "type": "text" + }, + "status": { + "type": "integer" + }, + "deliveryType": { + "type": "integer" + }, + "total": { + "type": "long" + }, + "closeType": { + "type": "integer" + }, + "payTime": { + "type": "date" + }, + "deliveryTime": { + "type": "date" + }, + "finallyTime": { + "type": "date" + }, + "cancelTime": { + "type": "date" + }, + "orderAddrId": { + "type": "long" + }, + "isPayed": { + "type": "integer" + }, + "deleteStatus": { + "type": "integer" + }, + "orderItems": { + "type": "nested", + "properties": { + "pic": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "spuName": { + "type": "text", + "analyzer": "ik_max_word", + "search_analyzer": "ik_smart" + }, + "skuName": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "count": { + "type": "integer" + }, + "price": { + "type": "long" + }, + "skuId": { + "type": "long" + }, + "orderItemId": { + "type": "long" + }, + "spuId": { + "type": "long" + }, + "shopId": { + "type": "long" + }, + "userId": { + "type": "long" + }, + "deliveryType": { + "type": "integer" + }, + "shopCartTime": { + "type": "date" + }, + "spuTotalAmount": { + "type": "long" + } + } + } + } + } +} +``` diff --git a/es/product.md b/es/product.md new file mode 100644 index 00000000..6d6a0e14 --- /dev/null +++ b/es/product.md @@ -0,0 +1,156 @@ + +商品搜索 mapping + +PUT product + +```json +{ + "mappings" : { + "properties" : { + "attrs" : { + "type" : "nested", + "properties" : { + "attrId" : { + "type" : "long" + }, + "attrName" : { + "type" : "keyword" + }, + "attrValueId" : { + "type" : "long" + }, + "attrValueName" : { + "type" : "keyword" + } + } + }, + "tags" : { + "type" : "nested", + "properties" : { + "tagId" : { + "type" : "long" + }, + "seq" : { + "type" : "integer" + } + } + }, + "brandId" : { + "type" : "long" + }, + "brandImg" : { + "type" : "keyword" + }, + "brandName" : { + "type" : "keyword" + }, + "code" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "commentNum" : { + "type" : "integer" + }, + "createTime" : { + "type" : "date" + }, + "hasStock" : { + "type" : "boolean" + }, + "imgUrls" : { + "type" : "keyword", + "index" : false, + "doc_values" : false + }, + "mainImgUrl" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "marketPriceFee" : { + "type" : "long" + }, + "priceFee" : { + "type" : "long" + }, + "saleNum" : { + "type" : "integer" + }, + "sellingPoint" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "shopId" : { + "type" : "long" + }, + "shopImg" : { + "type" : "keyword", + "index" : false, + "doc_values" : false + }, + "shopName" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "shopType" : { + "type" : "integer" + }, + "shopPrimaryCategoryId" : { + "type" : "long" + }, + "shopPrimaryCategoryName" : { + "type" : "keyword" + }, + "shopSecondaryCategoryId" : { + "type" : "long" + }, + "shopSecondaryCategoryName" : { + "type" : "keyword" + }, + "primaryCategoryId" : { + "type" : "long" + }, + "primaryCategoryName" : { + "type" : "keyword" + }, + "secondaryCategoryId" : { + "type" : "long" + }, + "secondaryCategoryName" : { + "type" : "keyword" + }, + "categoryId" : { + "type" : "long" + }, + "categoryName" : { + "type" : "keyword" + }, + "spuId" : { + "type" : "long" + }, + "spuName" : { + "type" : "text", + "analyzer" : "ik_max_word", + "search_analyzer" : "ik_smart" + }, + "spuStatus" : { + "type" : "integer" + }, + "success" : { + "type" : "boolean" + } + } + } +} +``` diff --git a/mall4cloud-api/mall4cloud-api-auth/pom.xml b/mall4cloud-api/mall4cloud-api-auth/pom.xml new file mode 100644 index 00000000..bd312396 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-auth + jar + 授权对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/bo/UserInfoInTokenBO.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/bo/UserInfoInTokenBO.java new file mode 100644 index 00000000..526e6ef1 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/bo/UserInfoInTokenBO.java @@ -0,0 +1,113 @@ +package com.mall4j.cloud.api.auth.bo; + +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; + +/** + * 保存在token信息里面的用户信息 + * + * com.mall4j.cloud.auth.service.impl.AuthAccountServiceImpl + * com.mall4j.cloud.auth.controller.LoginController + * @author FrozenWatermelon + * @date 2020/7/3 + */ +public class UserInfoInTokenBO { + + /** + * 用户在自己系统的用户id + */ + private Long userId; + + /** + * 全局唯一的id, + */ + private Long uid; + + /** + * 租户id (商家id) + */ + private Long tenantId; + + /** + * 系统类型 + * @see SysTypeEnum + */ + private Integer sysType; + + /** + * 是否是管理员 + */ + private Integer isAdmin; + + private String bizUserId; + + private String bizUid; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public String getBizUserId() { + return bizUserId; + } + + public void setBizUserId(String bizUserId) { + this.bizUserId = bizUserId; + } + + public String getBizUid() { + return bizUid; + } + + public void setBizUid(String bizUid) { + this.bizUid = bizUid; + } + + @Override + public String toString() { + return "UserInfoInTokenBO{" + + "userId=" + userId + + ", uid=" + uid + + ", tenantId=" + tenantId + + ", sysType=" + sysType + + ", isAdmin=" + isAdmin + + ", bizUserId='" + bizUserId + '\'' + + ", bizUid='" + bizUid + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/constant/SysTypeEnum.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/constant/SysTypeEnum.java new file mode 100644 index 00000000..406c84cc --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/constant/SysTypeEnum.java @@ -0,0 +1,37 @@ +package com.mall4j.cloud.api.auth.constant; + +/** + * 系统类型 + * @author FrozenWatermelon + * @date 2020/7/2 + */ +public enum SysTypeEnum { + + /** + * 普通用户系统 + */ + ORDINARY(0), + + /** + * 商家端 + */ + MULTISHOP(1), + + /** + * 平台端 + */ + PLATFORM(2), + + ; + + private final Integer value; + + public Integer value() { + return value; + } + + SysTypeEnum(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/dto/AuthAccountDTO.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/dto/AuthAccountDTO.java new file mode 100644 index 00000000..2101b169 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/dto/AuthAccountDTO.java @@ -0,0 +1,135 @@ +package com.mall4j.cloud.api.auth.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020/9/22 + */ +public class AuthAccountDTO { + + /** + * 用户名 + */ + @NotBlank(message = "username not blank") + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 创建ip + */ + private String createIp; + + /** + * 状态 1:启用 0:禁用 -1:删除 + */ + @NotNull(message = "status not null") + private Integer status; + + /** + * 系统类型见SysTypeEnum 0.普通用户系统 1.商家端 + */ + @NotNull(message = "sysType not null") + private Integer sysType; + + /** + * 用户id + */ + @NotNull(message = "userId not null") + private Long userId; + + /** + * 所属租户 + */ + @NotNull(message = "tenantId not null") + private Long tenantId; + + /** + * 是否是管理员 + */ + @NotNull(message = "isAdmin not null") + private Integer isAdmin; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCreateIp() { + return createIp; + } + + public void setCreateIp(String createIp) { + this.createIp = createIp; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "AuthAccountDTO{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", createIp='" + createIp + '\'' + + ", status=" + status + + ", sysType=" + sysType + + ", userId=" + userId + + ", tenantId=" + tenantId + + ", isAdmin=" + isAdmin + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/AccountFeignClient.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/AccountFeignClient.java new file mode 100644 index 00000000..1b672405 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/AccountFeignClient.java @@ -0,0 +1,95 @@ +package com.mall4j.cloud.api.auth.feign; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +/** + * @author FrozenWatermelon + * @date 2020/9/22 + */ +@FeignClient(value = "mall4cloud-auth",contextId ="account") +public interface AccountFeignClient { + + /** + * 保存统一账户 + * @param authAccountDTO 账户信息 + * @return Long uid + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/account") + ServerResponseEntity save(@RequestBody AuthAccountDTO authAccountDTO); + + /** + * 更新统一账户 + * @param authAccountDTO 账户信息 + * @return void + */ + @PutMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/account") + ServerResponseEntity update(@RequestBody AuthAccountDTO authAccountDTO); + + /** + * 更新账户状态 + * @param authAccountDTO 账户信息 + * @return void + */ + @PutMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/account/status") + ServerResponseEntity updateAuthAccountStatus(@RequestBody AuthAccountDTO authAccountDTO); + + /** + * 根据用户id和系统类型删除用户 + * @param userId 用户id + * @return void + */ + @DeleteMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/account/deleteByUserIdAndSysType") + ServerResponseEntity deleteByUserIdAndSysType(@RequestParam("userId")Long userId); + + /** + * 根据用户id和系统类型获取用户信息 + * @param userId 用户id + * @param sysType 系统类型 + * @return void + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/account/getByUserIdAndSysType") + ServerResponseEntity getByUserIdAndSysType(@RequestParam("userId") Long userId,@RequestParam("sysType") Integer sysType); + + /** + * 保存用户信息,生成token,返回前端 + * @param userInfoInTokenBO 账户信息 和社交账号信息 + * @return uid + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/storeTokenAndGetVo") + ServerResponseEntity storeTokenAndGetVo(@RequestBody UserInfoInTokenBO userInfoInTokenBO); + + /** + * 根据用户名和系统类型获取用户信息 + * @param username + * @param sysType + * @return + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/getByUsernameAndSysType") + ServerResponseEntity getByUsernameAndSysType(@RequestParam("userName") String username, @RequestParam("sysType") SysTypeEnum sysType); + + /** + * 根据用户id与用户类型更新用户信息 + * @param userInfoInTokenBO 新的用户信息 + * @param userId 用户id + * @param sysType 用户类型 + * @return + */ + @PutMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/accout/updateTenantIdByUserIdAndSysType") + ServerResponseEntity updateUserInfoByUserIdAndSysType(@RequestBody UserInfoInTokenBO userInfoInTokenBO, @RequestParam("userId") Long userId, @RequestParam("sysType") Integer sysType); + + /** + * 根据租户id查询商家信息 + * @param tenantId + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/account/getMerchantInfoByTenantId") + ServerResponseEntity getMerchantInfoByTenantId(@RequestParam("tenantId") Long tenantId); +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/TokenFeignClient.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/TokenFeignClient.java new file mode 100644 index 00000000..23e22321 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/feign/TokenFeignClient.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.api.auth.feign; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.constant.Auth; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author FrozenWatermelon + * @date 2020/7/15 + */ +@FeignClient(value = "mall4cloud-auth",contextId ="token") +public interface TokenFeignClient { + + /** + * 校验token并返回token保存的用户信息 + * @param accessToken accessToken + * @return token保存的用户信息 + */ + @GetMapping(value = Auth.CHECK_TOKEN_URI) + ServerResponseEntity checkToken(@RequestParam("accessToken") String accessToken); + +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/AuthAccountVO.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/AuthAccountVO.java new file mode 100644 index 00000000..3a9b9160 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/AuthAccountVO.java @@ -0,0 +1,77 @@ +package com.mall4j.cloud.api.auth.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/9/22 + */ +public class AuthAccountVO { + /** + * 全平台用户唯一id + */ + private Long uid; + + @ApiModelProperty("用户id") + private Long userId; + + @ApiModelProperty("用户名") + private String username; + + @ApiModelProperty("状态 1:启用 0:禁用 -1:删除") + private Integer status; + + @ApiModelProperty("创建ip") + private String createIp; + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public String getCreateIp() { + return createIp; + } + + public void setCreateIp(String createIp) { + this.createIp = createIp; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "AuthAccountVO{" + + "uid=" + uid + + ", userId=" + userId + + ", username='" + username + '\'' + + ", status=" + status + + ", createIp='" + createIp + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/TokenInfoVO.java b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/TokenInfoVO.java new file mode 100644 index 00000000..0b663cc3 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-auth/src/main/java/com/mall4j/cloud/api/auth/vo/TokenInfoVO.java @@ -0,0 +1,52 @@ +package com.mall4j.cloud.api.auth.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * token信息,该信息用户返回给前端,前端请求携带accessToken进行用户校验 + * + * @author FrozenWatermelon + * @date 2020/7/2 + */ +public class TokenInfoVO { + + @ApiModelProperty("accessToken") + private String accessToken; + + @ApiModelProperty("refreshToken") + private String refreshToken; + + @ApiModelProperty("在多少秒后过期") + private Integer expiresIn; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + } + + @Override + public String toString() { + return "TokenInfoVO{" + "accessToken='" + accessToken + '\'' + ", refreshToken='" + refreshToken + '\'' + + ", expiresIn=" + expiresIn + '}'; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-biz/pom.xml b/mall4cloud-api/mall4cloud-api-biz/pom.xml new file mode 100644 index 00000000..f5b48936 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-biz/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-biz + jar + biz对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-leaf/pom.xml b/mall4cloud-api/mall4cloud-api-leaf/pom.xml new file mode 100644 index 00000000..2b2b1e59 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-leaf/pom.xml @@ -0,0 +1,29 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-leaf + jar + 美团分布式id生成接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + io.seata + seata-spring-boot-starter + + + + + + diff --git a/mall4cloud-api/mall4cloud-api-leaf/src/main/java/com/mall4j/cloud/api/leaf/feign/SegmentFeignClient.java b/mall4cloud-api/mall4cloud-api-leaf/src/main/java/com/mall4j/cloud/api/leaf/feign/SegmentFeignClient.java new file mode 100644 index 00000000..9052b7b4 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-leaf/src/main/java/com/mall4j/cloud/api/leaf/feign/SegmentFeignClient.java @@ -0,0 +1,26 @@ +package com.mall4j.cloud.api.leaf.feign; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * + * @author FrozenWatermelon + * @date 2020/09/08 + */ +@FeignClient(value = "mall4cloud-leaf",contextId ="segment") +public interface SegmentFeignClient { + + /** + * 获取id + * @param key + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/segment") + ServerResponseEntity getSegmentId(@RequestParam("key") String key); + + +} diff --git a/mall4cloud-api/mall4cloud-api-multishop/pom.xml b/mall4cloud-api/mall4cloud-api-multishop/pom.xml new file mode 100644 index 00000000..7e4f41f4 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-multishop/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-multishop + jar + 店铺对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/bo/EsShopDetailBO.java b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/bo/EsShopDetailBO.java new file mode 100644 index 00000000..0b4a274a --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/bo/EsShopDetailBO.java @@ -0,0 +1,80 @@ +package com.mall4j.cloud.api.multishop.bo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 店铺详情VO + * + * @author FrozenWatermelon + * @date 2020-11-23 16:24:29 + */ +public class EsShopDetailBO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("店铺logo") + private String shopLogo; + + @ApiModelProperty("店铺状态(-1:未开通 0: 停业中 1:营业中)") + private Integer shopStatus; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + @Override + public String toString() { + return "EsShopDetailBO{" + + "shopId=" + shopId + + ", type=" + type + + ", shopName='" + shopName + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", shopStatus=" + shopStatus + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/IndexImgFeignClient.java b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/IndexImgFeignClient.java new file mode 100644 index 00000000..ff8799d8 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/IndexImgFeignClient.java @@ -0,0 +1,22 @@ +package com.mall4j.cloud.api.multishop.feign; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @Author lth + * @Date 2021/7/8 11:10 + */ +@FeignClient(value = "mall4cloud-multishop",contextId = "indexImg") +public interface IndexImgFeignClient { + + /** + * 根据商品d删除轮播图信息 + * @param spuId 商品id + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/indexImg/deleteBySpuId") + ServerResponseEntity deleteBySpuId(@RequestParam("spuId") Long spuId, @RequestParam("shopId") Long shopId); +} diff --git a/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/ShopDetailFeignClient.java b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/ShopDetailFeignClient.java new file mode 100644 index 00000000..6b626579 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/feign/ShopDetailFeignClient.java @@ -0,0 +1,62 @@ +package com.mall4j.cloud.api.multishop.feign; + +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +@FeignClient(value = "mall4cloud-multishop",contextId = "shopDetail") +public interface ShopDetailFeignClient { + + + /** + * 根据店铺id获取店铺名称 + * @param shopId 店铺id + * @return 店铺名称 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/shopDetail/getShopNameByShopId") + ServerResponseEntity getShopNameByShopId(@RequestParam("shopId") Long shopId); + + /** + * 根据店铺id获取店铺信息 + * @param shopId 店铺id + * @return 店铺信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/shopDetail/getShopByShopId") + ServerResponseEntity getShopByShopId(@RequestParam("shopId") Long shopId); + + /** + * 根据店铺id列表, 获取店铺列表信息 + * @param shopIds 店铺id列表 + * @return 店铺列表信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/shopDetail/listByShopIds") + ServerResponseEntity> listByShopIds(@RequestParam("shopIds") List shopIds); + + /** + * 获取店铺信息及扩展信息 + * @param shopId 店铺id + * @return 店铺信息及扩展信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/shopDetail/getShopExtension") + ServerResponseEntity shopExtensionData(@RequestParam("shopId") Long shopId); + + /** + * 获取店铺信息及扩展信息 + * @param shopIds 店铺ids + * @param shopName 店铺名称 + * @return 店铺信息列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/shopDetail/getShopDetailByShopIdAndShopName") + ServerResponseEntity> getShopDetailByShopIdAndShopName(@RequestParam("shopIds") List shopIds, + @RequestParam(value = "shopName",defaultValue = "") String shopName); +} diff --git a/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/vo/ShopDetailVO.java b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/vo/ShopDetailVO.java new file mode 100644 index 00000000..a1ed2635 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-multishop/src/main/java/com/mall4j/cloud/api/multishop/vo/ShopDetailVO.java @@ -0,0 +1,171 @@ +package com.mall4j.cloud.api.multishop.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 店铺详情VO + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public class ShopDetailVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("店铺简介") + private String intro; + + @ApiModelProperty("店铺logo(可修改)") + @JsonSerialize(using = ImgJsonSerializer.class) + private String shopLogo; + + @ApiModelProperty("店铺状态(-1:已删除 0: 停业中 1:营业中)") + private Integer shopStatus; + + @ApiModelProperty("营业执照") + @JsonSerialize(using = ImgJsonSerializer.class) + private String businessLicense; + + @ApiModelProperty("身份证正面") + @JsonSerialize(using = ImgJsonSerializer.class) + private String identityCardFront; + + @ApiModelProperty("身份证反面") + @JsonSerialize(using = ImgJsonSerializer.class) + private String identityCardLater; + + @ApiModelProperty("移动端背景图") + @JsonSerialize(using = ImgJsonSerializer.class) + private String mobileBackgroundPic; + + @ApiModelProperty(value = "用户名",required=true) + private String username; + + @ApiModelProperty(value = "密码",required=true) + private String password; + + public String getMobileBackgroundPic() { + return mobileBackgroundPic; + } + + public void setMobileBackgroundPic(String mobileBackgroundPic) { + this.mobileBackgroundPic = mobileBackgroundPic; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getIntro() { + return intro; + } + + public void setIntro(String intro) { + this.intro = intro; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + public String getBusinessLicense() { + return businessLicense; + } + + public void setBusinessLicense(String businessLicense) { + this.businessLicense = businessLicense; + } + + public String getIdentityCardFront() { + return identityCardFront; + } + + public void setIdentityCardFront(String identityCardFront) { + this.identityCardFront = identityCardFront; + } + + public String getIdentityCardLater() { + return identityCardLater; + } + + public void setIdentityCardLater(String identityCardLater) { + this.identityCardLater = identityCardLater; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return "ShopDetailVO{" + + "shopId=" + shopId + + ", type=" + type + + ", shopName='" + shopName + '\'' + + ", intro='" + intro + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", shopStatus=" + shopStatus + + ", businessLicense='" + businessLicense + '\'' + + ", identityCardFront='" + identityCardFront + '\'' + + ", identityCardLater='" + identityCardLater + '\'' + + ", mobileBackgroundPic='" + mobileBackgroundPic + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/pom.xml b/mall4cloud-api/mall4cloud-api-order/pom.xml new file mode 100644 index 00000000..b2309ba2 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-order + jar + 订单对内接口 + + + com.mall4j.cloud + mall4cloud-common-order + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderBO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderBO.java new file mode 100644 index 00000000..40ac839a --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderBO.java @@ -0,0 +1,316 @@ +package com.mall4j.cloud.api.order.bo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2021/2/5 + */ +public class EsOrderBO { + + @ApiModelProperty(value = "订单项",required=true) + private List orderItems; + + @ApiModelProperty(value = "用户id",required=true) + private Long userId; + + @ApiModelProperty(value = "订单号",required=true) + private Long orderId; + + @ApiModelProperty(value = "总价",required=true) + private Long actualTotal; + + @ApiModelProperty(value = "订单状态",required=true) + private Integer status; + + @ApiModelProperty(value = "配送类型 :无需快递",required=true) + private Integer deliveryType; + + @ApiModelProperty(value = "店铺名称",required=true) + private String shopName; + + @ApiModelProperty(value = "店铺id",required=true) + private Long shopId; + + @ApiModelProperty(value = "订单创建时间",required=true) + private Date createTime; + + @ApiModelProperty(value = "商品总数",required=true) + private Integer allCount; + + @ApiModelProperty(value = "收货人姓名") + private String consignee; + + @ApiModelProperty(value = "收货人手机号") + private String mobile; + /** + * 用户订单地址Id + */ + private Long orderAddrId; + + /** + * 总值 + */ + private Long total; + + /** + * 支付方式 请参考枚举PayType + */ + private Integer payType; + + /** + * 订单关闭原因 1-超时未支付 4-买家取消 15-已通过货到付款交易 + */ + private Integer closeType; + + /** + * 发货时间 + */ + private Date updateTime; + + /** + * 付款时间 + */ + private Date payTime; + + /** + * 发货时间 + */ + private Date deliveryTime; + + /** + * 完成时间 + */ + private Date finallyTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 是否已支付,1.已支付0.未支付 + */ + private Integer isPayed; + + /** + * 用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除 + */ + private Integer deleteStatus; + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getActualTotal() { + return actualTotal; + } + + public void setActualTotal(Long actualTotal) { + this.actualTotal = actualTotal; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getPayType() { + return payType; + } + + public void setPayType(Integer payType) { + this.payType = payType; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Integer getDeleteStatus() { + return deleteStatus; + } + + public void setDeleteStatus(Integer deleteStatus) { + this.deleteStatus = deleteStatus; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "EsOrderBO{" + + "orderItems=" + orderItems + + ", userId=" + userId + + ", orderId=" + orderId + + ", actualTotal=" + actualTotal + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", shopName='" + shopName + '\'' + + ", shopId=" + shopId + + ", createTime=" + createTime + + ", allCount=" + allCount + + ", consignee='" + consignee + '\'' + + ", mobile='" + mobile + '\'' + + ", orderAddrId=" + orderAddrId + + ", total=" + total + + ", payType=" + payType + + ", closeType=" + closeType + + ", updateTime=" + updateTime + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", isPayed=" + isPayed + + ", deleteStatus=" + deleteStatus + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderItemBO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderItemBO.java new file mode 100644 index 00000000..d5f56ac9 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/EsOrderItemBO.java @@ -0,0 +1,183 @@ +package com.mall4j.cloud.api.order.bo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * @author FrozenWatermelon + * @date 2021/2/5 + */ +public class EsOrderItemBO { + @ApiModelProperty(value = "商品图片", required = true) + private String pic; + + @ApiModelProperty(value = "商品名称", required = true) + private String spuName; + + @ApiModelProperty(value = "商品数量", required = true) + private Integer count; + + @ApiModelProperty(value = "商品价格", required = true) + private Long price; + + @ApiModelProperty(value = "skuId", required = true) + private Long skuId; + + @ApiModelProperty(value = "skuName", required = true) + private String skuName; + + @ApiModelProperty(value = "订单项id", required = true) + private Long orderItemId; + + @ApiModelProperty(value = "商品id", required = true) + private Long spuId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 用户Id + */ + private Long userId; + + /** + * 单个orderItem的配送类型 :无需快递 + */ + private Integer deliveryType; + + /** + * 加入购物车时间 + */ + private Date shopCartTime; + + /** + * 商品总金额 + */ + private Long spuTotalAmount; + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Date getShopCartTime() { + return shopCartTime; + } + + public void setShopCartTime(Date shopCartTime) { + this.shopCartTime = shopCartTime; + } + + public Long getSpuTotalAmount() { + return spuTotalAmount; + } + + public void setSpuTotalAmount(Long spuTotalAmount) { + this.spuTotalAmount = spuTotalAmount; + } + + @Override + public String toString() { + return "EsOrderItemBO{" + + "pic='" + pic + '\'' + + ", spuName='" + spuName + '\'' + + ", count=" + count + + ", price=" + price + + ", skuId=" + skuId + + ", skuName='" + skuName + '\'' + + ", orderItemId=" + orderItemId + + ", spuId=" + spuId + + ", shopId=" + shopId + + ", userId=" + userId + + ", deliveryType=" + deliveryType + + ", shopCartTime=" + shopCartTime + + ", spuTotalAmount=" + spuTotalAmount + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderSimpleAmountInfoBO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderSimpleAmountInfoBO.java new file mode 100644 index 00000000..3856bf7b --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderSimpleAmountInfoBO.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.api.order.bo; + +/** + * @author FrozenWatermelon + * @date 2020/12/30 + */ +public class OrderSimpleAmountInfoBO { + + private Long orderId; + + private Long shopId; + + /** + * 实际总值 + */ + private Long actualTotal; + + /** + * 订单状态 + */ + private Integer status; + + /** + * 订单关闭原因 + */ + private Integer closeType; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getActualTotal() { + return actualTotal; + } + + public void setActualTotal(Long actualTotal) { + this.actualTotal = actualTotal; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + @Override + public String toString() { + return "OrderSimpleAmountInfoBO{" + + "orderId=" + orderId + + ", shopId=" + shopId + + ", actualTotal=" + actualTotal + + ", status=" + status + + ", closeType=" + closeType + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderStatusBO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderStatusBO.java new file mode 100644 index 00000000..efb0aa6b --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/bo/OrderStatusBO.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.api.order.bo; + +/** + * @author FrozenWatermelon + * @date 2020/12/30 + */ +public class OrderStatusBO { + + private Long orderId; + + private Integer status; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "OrderStatusBO{" + + "orderId=" + orderId + + ", status=" + status + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/DeliveryType.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/DeliveryType.java new file mode 100644 index 00000000..0ca6bf06 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/DeliveryType.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.api.order.constant; + +/** + * 配送类型 + * @author FrozenWatermelon + */ +public enum DeliveryType { + + + /** + * 无需快递 + */ + NOT_DELIVERY(3); + + private final Integer num; + + public Integer value() { + return num; + } + + DeliveryType(Integer num) { + this.num = num; + } + + public static DeliveryType instance(Integer value) { + DeliveryType[] enums = values(); + for (DeliveryType statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/OrderStatus.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/OrderStatus.java new file mode 100644 index 00000000..8125308e --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/constant/OrderStatus.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.api.order.constant; + +/** + * 订单状态 + * @author FrozenWatermelon + * @date 2020/12/18 + */ +public enum OrderStatus { + + /** + * 没有付款.待付款 + */ + UNPAY(1), + + /** + * 已经付款,但卖家没有发货.待发货 + */ + PADYED(2), + + /** + * 发货,导致实际库存减少,没有确认收货.待收货 + */ + CONSIGNMENT(3), + + /** + * 订单确认收货成功,购买数增加1. + */ + SUCCESS(5), + + /** + * 交易失败,还原库存 + */ + CLOSE(6); + + private final Integer num; + + public Integer value() { + return num; + } + + OrderStatus(Integer num) { + this.num = num; + } + + public static OrderStatus instance(Integer value) { + OrderStatus[] enums = values(); + for (OrderStatus statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderDTO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderDTO.java new file mode 100644 index 00000000..816fae12 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderDTO.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.api.order.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 订单快递信息DTO + * + * @author FrozenWatermelon + * @date 2020-12-07 15:10:00 + */ +public class DeliveryOrderDTO{ + + @ApiModelProperty(value = "deliveryOrderId") + private Long deliveryOrderId; + + @NotNull(message="订单号不能为空") + @ApiModelProperty(value = "订单号",required=true) + private Long orderId; + + @NotNull(message="发货方式不能为空") + @ApiModelProperty(value = "发货方式",required=true) + private Integer deliveryType; + + private List selectOrderItems; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public List getSelectOrderItems() { + return selectOrderItems; + } + + public void setSelectOrderItems(List selectOrderItems) { + this.selectOrderItems = selectOrderItems; + } + + public Long getDeliveryOrderId() { + return deliveryOrderId; + } + + public void setDeliveryOrderId(Long deliveryOrderId) { + this.deliveryOrderId = deliveryOrderId; + } + + @Override + public String toString() { + return "DeliveryOrderDTO{" + + "deliveryOrderId='" + deliveryOrderId + '\'' + + "orderNumber='" + orderId + '\'' + + ", deliveryType=" + deliveryType + + ", selectOrderItems=" + selectOrderItems + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderItemDTO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderItemDTO.java new file mode 100644 index 00000000..f0167e71 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/dto/DeliveryOrderItemDTO.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.api.order.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 物流订单项信息DTO + * + * @author FrozenWatermelon + * @date 2020-12-07 15:10:00 + */ +public class DeliveryOrderItemDTO{ + + @ApiModelProperty("id") + private Long orderItemId; + + @ApiModelProperty("商品图片") + private String pic; + + @ApiModelProperty("商品名称") + private String spuName; + + @ApiModelProperty("发货改变的数量") + private Integer changeNum; + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Integer getChangeNum() { + return changeNum; + } + + public void setChangeNum(Integer changeNum) { + this.changeNum = changeNum; + } + + @Override + public String toString() { + return "DeliveryOrderItemDTO{" + + "orderItemId=" + orderItemId + + ", pic='" + pic + '\'' + + ", spuName='" + spuName + '\'' + + ", changeNum=" + changeNum + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/feign/OrderFeignClient.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/feign/OrderFeignClient.java new file mode 100644 index 00000000..30193bbc --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/feign/OrderFeignClient.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.api.order.feign; + +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.bo.OrderSimpleAmountInfoBO; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +@FeignClient(value = "mall4cloud-order",contextId = "order") +public interface OrderFeignClient { + + + /** + * 如果订单没有被取消的话,获取订单金额,否之会获取失败 + * + * @param orderIds 订单id列表 + * @return 订单金额 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/ordgetOrdersAmountAndIfNoCanceler") + ServerResponseEntity getOrdersAmountAndIfNoCancel(@RequestParam("orderIds") List orderIds); + + /** + * 获取订单状态,如果订单状态不存在,则说明订单没有创建 + * + * @param orderIds 订单id列表 + * @return 订单状态 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/getOrdersStatus") + ServerResponseEntity> getOrdersStatus(@RequestParam("orderIds") List orderIds); + + /** + * 获取订单中的金额信息 + * + * @param orderIds 订单id列表 + * @return 订单中的金额信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/getOrdersSimpleAmountInfo") + ServerResponseEntity> getOrdersSimpleAmountInfo(@RequestParam("orderIds") List orderIds); + + + /** + * 获取订单需要保存到es中的数据 + * + * @param orderId 订单id + * @return es中的数据 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/getEsOrder") + ServerResponseEntity getEsOrder(@RequestParam("orderId")Long orderId); + + /** + * 支付时更新订单状态 + * @param orderIds 订单id列表 + * @return null + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/updateOrderState") + ServerResponseEntity updateOrderState(@RequestParam("orderIds") List orderIds); +} diff --git a/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/vo/OrderAmountVO.java b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/vo/OrderAmountVO.java new file mode 100644 index 00000000..5d40cb13 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-order/src/main/java/com/mall4j/cloud/api/order/vo/OrderAmountVO.java @@ -0,0 +1,28 @@ +package com.mall4j.cloud.api.order.vo; + +/** + * @author FrozenWatermelon + * @date 2020/12/25 + */ +public class OrderAmountVO { + + /** + * 支付金额 + */ + private Long payAmount; + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + @Override + public String toString() { + return "OrderAmountVO{" + + "payAmount=" + payAmount + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-platform/pom.xml b/mall4cloud-api/mall4cloud-api-platform/pom.xml new file mode 100644 index 00000000..04da0945 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-platform/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-platform + jar + 平台对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-platform/src/main/java/com/mall4j/cloud/api/platform/feign/ConfigFeignClient.java b/mall4cloud-api/mall4cloud-api-platform/src/main/java/com/mall4j/cloud/api/platform/feign/ConfigFeignClient.java new file mode 100644 index 00000000..b5cfd536 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-platform/src/main/java/com/mall4j/cloud/api/platform/feign/ConfigFeignClient.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.api.platform.feign; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +@FeignClient(value = "mall4cloud-platform",contextId = "config") +public interface ConfigFeignClient { + + + /** + * 获取配置信息 + * @param key key + * @return 配置信息json + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/config/getConfig") + ServerResponseEntity getConfig(@RequestParam("key") String key); + +} diff --git a/mall4cloud-api/mall4cloud-api-product/pom.xml b/mall4cloud-api/mall4cloud-api-product/pom.xml new file mode 100644 index 00000000..6b9ac517 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/pom.xml @@ -0,0 +1,33 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-product + jar + 商品对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsAttrBO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsAttrBO.java new file mode 100644 index 00000000..d1f36463 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsAttrBO.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.api.product.bo; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +public class EsAttrBO { + + /** + * 规格id + */ + private Long attrId; + + /** + * 规格名 + */ + private String attrName; + + /** + * 规格值id + */ + private Long attrValueId; + + /** + * 规格值名称 + */ + private String attrValueName; + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + @Override + public String toString() { + return "EsAttrBO{" + + "attrId=" + attrId + + ", attrName='" + attrName + '\'' + + ", attrValueId=" + attrValueId + + ", attrValueName='" + attrValueName + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsProductBO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsProductBO.java new file mode 100644 index 00000000..53e80deb --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/bo/EsProductBO.java @@ -0,0 +1,441 @@ +package com.mall4j.cloud.api.product.bo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; + +import java.util.Date; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +public class EsProductBO { + + /** + * 商品id + */ + private Long spuId; + + /** + * 商品名称 + */ + private String spuName; + + /** + * 卖点 + */ + private String sellingPoint; + + /** + * 商品售价 + */ + private Long priceFee; + + /** + * 市场价,整数方式保存 + */ + private Long marketPriceFee; + + /** + * 商品介绍主图 + */ + @JsonSerialize(using = ImgJsonSerializer.class) + private String mainImgUrl; + + /** + * 店铺名称 搜索华为的时候,可以把华为的旗舰店搜索出来 + */ + private String shopName; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 店铺logo + */ + private String shopImg; + + /** + * 店铺类型1自营店 2普通店 + */ + private Integer shopType; + + /** + * 商品状态 + */ + private Integer spuStatus; + + /** + * 是否有库存 + */ + private Boolean hasStock; + + /** + * 库存 + */ + private Integer stock; + + /** + * 销量 + */ + private Integer saleNum; + + /** + * 商品创建时间 + */ + private Date createTime; + + /** + * 品牌名称 + */ + private String brandName; + + /** + * 品牌id + */ + private Integer brandId; + + /** + * 商品序号 + */ + private Integer seq; + + /** + * 品牌图片 + */ + private String brandImg; + + /** + * 分类id + */ + private Long categoryId; + + /** + * 分类名称 + */ + private String categoryName; + + /** + * 商家一级分类id + */ + private Long shopPrimaryCategoryId; + + /** + * 商家一级分类名称 + */ + private String shopPrimaryCategoryName; + + /** + * 商家二级分类id + */ + private Long shopSecondaryCategoryId; + + /** + * 商家二级分类名称 + */ + private String shopSecondaryCategoryName; + + /** + * 平台一级分类id + */ + private Long primaryCategoryId; + + /** + * 平台一级分类名称 + */ + private String primaryCategoryName; + + /** + * 平台二级分类id + */ + private Long secondaryCategoryId; + + /** + * 平台二级分类名称 + */ + private String secondaryCategoryName; + + /** + * 商品用于搜索的规格属性 + */ + private List attrs; + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSpuStatus() { + return spuStatus; + } + + public void setSpuStatus(Integer spuStatus) { + this.spuStatus = spuStatus; + } + + public Boolean getHasStock() { + return hasStock; + } + + public void setHasStock(Boolean hasStock) { + this.hasStock = hasStock; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + this.brandName = brandName; + } + + public Integer getBrandId() { + return brandId; + } + + public void setBrandId(Integer brandId) { + this.brandId = brandId; + } + + public String getBrandImg() { + return brandImg; + } + + public void setBrandImg(String brandImg) { + this.brandImg = brandImg; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public Long getShopPrimaryCategoryId() { + return shopPrimaryCategoryId; + } + + public void setShopPrimaryCategoryId(Long shopPrimaryCategoryId) { + this.shopPrimaryCategoryId = shopPrimaryCategoryId; + } + + public String getShopPrimaryCategoryName() { + return shopPrimaryCategoryName; + } + + public void setShopPrimaryCategoryName(String shopPrimaryCategoryName) { + this.shopPrimaryCategoryName = shopPrimaryCategoryName; + } + + public Long getShopSecondaryCategoryId() { + return shopSecondaryCategoryId; + } + + public void setShopSecondaryCategoryId(Long shopSecondaryCategoryId) { + this.shopSecondaryCategoryId = shopSecondaryCategoryId; + } + + public String getShopSecondaryCategoryName() { + return shopSecondaryCategoryName; + } + + public void setShopSecondaryCategoryName(String shopSecondaryCategoryName) { + this.shopSecondaryCategoryName = shopSecondaryCategoryName; + } + + public List getAttrs() { + return attrs; + } + + public void setAttrs(List attrs) { + this.attrs = attrs; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getShopImg() { + return shopImg; + } + + public void setShopImg(String shopImg) { + this.shopImg = shopImg; + } + + public Long getPrimaryCategoryId() { + return primaryCategoryId; + } + + public void setPrimaryCategoryId(Long primaryCategoryId) { + this.primaryCategoryId = primaryCategoryId; + } + + public String getPrimaryCategoryName() { + return primaryCategoryName; + } + + public void setPrimaryCategoryName(String primaryCategoryName) { + this.primaryCategoryName = primaryCategoryName; + } + + public Long getSecondaryCategoryId() { + return secondaryCategoryId; + } + + public void setSecondaryCategoryId(Long secondaryCategoryId) { + this.secondaryCategoryId = secondaryCategoryId; + } + + public String getSecondaryCategoryName() { + return secondaryCategoryName; + } + + public Integer getShopType() { + return shopType; + } + + public void setShopType(Integer shopType) { + this.shopType = shopType; + } + + public void setSecondaryCategoryName(String secondaryCategoryName) { + this.secondaryCategoryName = secondaryCategoryName; + } + + @Override + public String toString() { + return "EsProductBO{" + + "spuId=" + spuId + + ", spuName='" + spuName + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", shopName='" + shopName + '\'' + + ", shopId=" + shopId + + ", shopImg='" + shopImg + '\'' + + ", shopType=" + shopType + + ", spuStatus=" + spuStatus + + ", hasStock=" + hasStock + + ", stock=" + stock + + ", saleNum=" + saleNum + + ", createTime=" + createTime + + ", brandName='" + brandName + '\'' + + ", brandId=" + brandId + + ", seq=" + seq + + ", brandImg='" + brandImg + '\'' + + ", categoryId=" + categoryId + + ", categoryName='" + categoryName + '\'' + + ", shopPrimaryCategoryId=" + shopPrimaryCategoryId + + ", shopPrimaryCategoryName='" + shopPrimaryCategoryName + '\'' + + ", shopSecondaryCategoryId=" + shopSecondaryCategoryId + + ", shopSecondaryCategoryName='" + shopSecondaryCategoryName + '\'' + + ", primaryCategoryId=" + primaryCategoryId + + ", primaryCategoryName='" + primaryCategoryName + '\'' + + ", secondaryCategoryId=" + secondaryCategoryId + + ", secondaryCategoryName='" + secondaryCategoryName + '\'' + + ", attrs=" + attrs + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/constant/CategoryLevel.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/constant/CategoryLevel.java new file mode 100644 index 00000000..01b7cf9c --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/constant/CategoryLevel.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.api.product.constant; + +/** + * 等级 + * @author yxf + * @date 2020/11/20 + */ +public enum CategoryLevel { + + /** + * 第一级 + */ + First(0), + + /** + * 第二级 + */ + SECOND(1), + + /** + * 第三级 + */ + THIRD(2) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + CategoryLevel(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/ShopCartItemDTO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/ShopCartItemDTO.java new file mode 100644 index 00000000..ce103654 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/ShopCartItemDTO.java @@ -0,0 +1,74 @@ +package com.mall4j.cloud.api.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * 购物车物品参数 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class ShopCartItemDTO { + + @NotNull(message = "产品ID不能为空") + @ApiModelProperty(value = "产品ID",required=true) + private Long spuId; + + @NotNull(message = "skuId不能为空") + @ApiModelProperty(value = "skuId",required=true) + private Long skuId; + + @NotNull(message = "商品数量不能为空") + @Min(value = 1,message = "商品数量不能为空") + @ApiModelProperty(value = "商品数量",required=true) + private Integer count; + + @NotNull(message = "店铺id不能为空") + @ApiModelProperty(value = "店铺id",required=true) + private Long shopId; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + @Override + public String toString() { + return "OrderItemDTO{" + + "spuId=" + spuId + + ", skuId=" + skuId + + ", count=" + count + + ", shopId=" + shopId + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SkuStockLockDTO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SkuStockLockDTO.java new file mode 100644 index 00000000..c2b62925 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SkuStockLockDTO.java @@ -0,0 +1,82 @@ +package com.mall4j.cloud.api.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020/12/22 + */ +public class SkuStockLockDTO { + + @NotNull(message = "产品ID不能为空") + @ApiModelProperty(value = "产品ID",required=true) + private Long spuId; + + @NotNull(message = "skuId不能为空") + @ApiModelProperty(value = "skuId",required=true) + private Long skuId; + + @NotNull(message = "orderId不能为空") + @ApiModelProperty(value = "orderId",required=true) + private Long orderId; + + @NotNull(message = "商品数量不能为空") + @Min(value = 1,message = "商品数量不能为空") + @ApiModelProperty(value = "商品数量",required=true) + private Integer count; + + public SkuStockLockDTO() { + } + + public SkuStockLockDTO(Long spuId, Long skuId, Long orderId, Integer count) { + this.spuId = spuId; + this.skuId = skuId; + this.orderId = orderId; + this.count = count; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public String toString() { + return "SkuStockLockDTO{" + + "spuId=" + spuId + + ", skuId=" + skuId + + ", orderId=" + orderId + + ", count=" + count + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SpuUpdateDTO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SpuUpdateDTO.java new file mode 100644 index 00000000..1a4ab6fd --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/dto/SpuUpdateDTO.java @@ -0,0 +1,171 @@ +package com.mall4j.cloud.api.product.dto; + +import java.io.Serializable; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +public class SpuUpdateDTO implements Serializable { + + /** + * 分类id + */ + private Long categoryId; + + /** + * 分类id列表 + */ + private List categoryIds; + + /** + * 分类名称 + */ + private String categoryName; + + /** + * 分类等级 + */ + private Integer categoryLevel; + + /** + * 商品状态 + */ + private Integer status; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 店铺名称 搜索华为的时候,可以把华为的旗舰店搜索出来 + */ + private String shopName; + + /** + * 店铺logo + */ + private String shopImg; + + /** + * 品牌id + */ + private Long brandId; + + /** + * 品牌图片 + */ + private String brandImg; + + /** + * 品牌名称 + */ + private String brandName; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public List getCategoryIds() { + return categoryIds; + } + + public void setCategoryIds(List categoryIds) { + this.categoryIds = categoryIds; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public Integer getCategoryLevel() { + return categoryLevel; + } + + public void setCategoryLevel(Integer categoryLevel) { + this.categoryLevel = categoryLevel; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopImg() { + return shopImg; + } + + public void setShopImg(String shopImg) { + this.shopImg = shopImg; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + this.brandName = brandName; + } + + public String getBrandImg() { + return brandImg; + } + + public void setBrandImg(String brandImg) { + this.brandImg = brandImg; + } + + @Override + public String toString() { + return "SpuUpdateDTO{" + + "categoryId=" + categoryId + + ", categoryIds=" + categoryIds + + ", categoryName='" + categoryName + '\'' + + ", categoryLevel=" + categoryLevel + + ", status=" + status + + ", shopId=" + shopId + + ", shopName='" + shopName + '\'' + + ", shopImg='" + shopImg + '\'' + + ", brandId=" + brandId + + ", brandImg='" + brandImg + '\'' + + ", brandName='" + brandName + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/CategoryFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/CategoryFeignClient.java new file mode 100644 index 00000000..c34118cc --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/CategoryFeignClient.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author lhd + * @date 2020/12/23 + */ +@FeignClient(value = "mall4cloud-product",contextId = "category") +public interface CategoryFeignClient { + + /** + * 获取所有一级分类信息 + * @return 一级分类信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/category/listByOneLevel") + ServerResponseEntity> listByOneLevel(); + + /** + * 根据上级id,获取子分类id列表 + * @param categoryId + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/category/listCategoryId") + ServerResponseEntity> listCategoryId(@RequestParam("categoryId") Long categoryId); +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ProductFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ProductFeignClient.java new file mode 100644 index 00000000..8613b345 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ProductFeignClient.java @@ -0,0 +1,59 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +@FeignClient(value = "mall4cloud-product",contextId = "product") +public interface ProductFeignClient { + + /** + * 通过spuId需要搜索的商品 + * @param spuId spuid + * @return es保存的商品信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/product/loadEsProductBO") + ServerResponseEntity loadEsProductBO(@RequestParam("spuId") Long spuId); + + /** + * 根据平台categoryId,获取spuId列表 + * @param shopCategoryIds + * @return spuId列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/product/getSpuIdsByShopCategoryIds") + ServerResponseEntity> getSpuIdsByShopCategoryIds(@RequestParam("shopCategoryIds")List shopCategoryIds); + + /** + * 根据categoryId列表,获取spuId列表 + * @param categoryIds + * @return spuId列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/product/getSpuIdsByCategoryIds") + ServerResponseEntity> getSpuIdsByCategoryIds(@RequestParam("categoryIds")List categoryIds); + + /** + * 根据brandId,获取spuId列表 + * @param brandId + * @return spuId列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/product/getSpuIdsByBrandId") + ServerResponseEntity> getSpuIdsByBrandId(@RequestParam("brandId")Long brandId); + + /** + * 根据店铺id,获取spuId列表 + * @param shopId + * @return spuId列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/product/getSpuIdsByShopId") + ServerResponseEntity> getSpuIdsByShopId(@RequestParam("shopId")Long shopId); + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ShopCartFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ShopCartFeignClient.java new file mode 100644 index 00000000..01ca4aae --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/ShopCartFeignClient.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +@FeignClient(value = "mall4cloud-product",contextId = "shopCart") +public interface ShopCartFeignClient { + + /** + * 获取购物项 + * @return 购物项 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/shopCart/getById") + ServerResponseEntity> getCheckedShopCartItems(); + + /** + * 通过购物车id删除用户购物车物品 + * @param shopCartItemIds 购物车id + * @return + */ + @DeleteMapping("/delete_item") + ServerResponseEntity deleteItem(@RequestBody List shopCartItemIds); +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuFeignClient.java new file mode 100644 index 00000000..a7d0a78d --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuFeignClient.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +@FeignClient(value = "mall4cloud-product",contextId = "sku") +public interface SkuFeignClient { + + /** + * 通过skuId获取sku信息 + * @param skuId skuId + * @return sku信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/sku/getById") + ServerResponseEntity getById(@RequestParam("skuId") Long skuId); + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuStockLockFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuStockLockFeignClient.java new file mode 100644 index 00000000..096f6a07 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SkuStockLockFeignClient.java @@ -0,0 +1,27 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.api.product.dto.SkuStockLockDTO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/12/22 + */ +@FeignClient(value = "mall4cloud-product",contextId = "skuStockLock") +public interface SkuStockLockFeignClient { + + /** + * 锁定库存 + * @param skuStockLocks 参数 + * @return 是否成功 + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/skuStockLock/lock") + ServerResponseEntity lock(@RequestBody List skuStockLocks); + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SpuFeignClient.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SpuFeignClient.java new file mode 100644 index 00000000..b45fd946 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/feign/SpuFeignClient.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.api.product.feign; + +import com.mall4j.cloud.api.product.vo.SpuAndSkuVO; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/12 + */ +@FeignClient(value = "mall4cloud-product",contextId = "spu") +public interface SpuFeignClient { + + /** + * 通过spuId需要搜索的商品 + * @param spuId spuid + * @return 商品信息(spuId,name,mainImgUrl) + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/spu/getById") + ServerResponseEntity getById(@RequestParam("spuId") Long spuId); + + /** + * 通过spuId需要搜索的商品 + * @param spuId spuId + * @param skuId skuId + * @return 商品信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/spu/getSpuAndSkuById") + ServerResponseEntity getSpuAndSkuById(@RequestParam("spuId") Long spuId,@RequestParam("skuId") Long skuId); + + + /** + * 根据spuId获取spu列表(所需字段:spuId、shopId、name、mainImgUrl) + * 根据店铺id获取spu列表 + * @param spuIds 商品ids + * @param spuName 商品名称 + * @param isFailure 是否失效 + * @return 商品列表信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/spu/getSpusBySpuIds") + ServerResponseEntity> getSpusBySpuIds(@RequestParam("spuIds") List spuIds,@RequestParam(value = "spuName",defaultValue = "") String spuName, + @RequestParam(value = "isFailure",defaultValue = "1") Integer isFailure); + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/manager/ShopCartAdapter.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/manager/ShopCartAdapter.java new file mode 100644 index 00000000..59084083 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/manager/ShopCartAdapter.java @@ -0,0 +1,140 @@ +package com.mall4j.cloud.api.product.manager; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.mall4j.cloud.api.multishop.feign.ShopDetailFeignClient; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.api.product.dto.ShopCartItemDTO; +import com.mall4j.cloud.api.product.feign.ShopCartFeignClient; +import com.mall4j.cloud.api.product.feign.SpuFeignClient; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.api.product.vo.SpuAndSkuVO; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.order.vo.ShopCartVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.BooleanUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 购物车适配器 + * @author FrozenWatermelon + * @date 2020/12/07 + */ +@Component +public class ShopCartAdapter { + + @Autowired + private SpuFeignClient spuFeignClient; + + @Autowired + private ShopCartFeignClient shopCartFeignClient; + + @Autowired + private ShopDetailFeignClient shopDetailFeignClient; + + /** + * 获取购物项组装信息 + * @param shopCartItemParam 购物项参数 + * @return 购物项组装信息 + */ + public ServerResponseEntity> getShopCartItems(ShopCartItemDTO shopCartItemParam) { + ServerResponseEntity> shopCartItemResponse; + // 当立即购买时,没有提交的订单是没有购物车信息的 + if (shopCartItemParam != null) { + shopCartItemResponse = conversionShopCartItem(shopCartItemParam); + } + // 从购物车提交订单 + else { + shopCartItemResponse = shopCartFeignClient.getCheckedShopCartItems(); + } + if (!shopCartItemResponse.isSuccess()) { + return ServerResponseEntity.transform(shopCartItemResponse); + } + // 请选择您需要的商品加入购物车 + if (CollectionUtil.isEmpty(shopCartItemResponse.getData())) { + return ServerResponseEntity.fail(ResponseEnum.SHOP_CART_NOT_EXIST); + } + // 返回购物车选择的商品信息 + return shopCartItemResponse; + } + + /** + * 将参数转换成组装好的购物项 + * @param shopCartItemParam 购物项参数 + * @return 组装好的购物项 + */ + public ServerResponseEntity> conversionShopCartItem(ShopCartItemDTO shopCartItemParam){ + ServerResponseEntity spuAndSkuResponse = spuFeignClient.getSpuAndSkuById(shopCartItemParam.getSpuId(),shopCartItemParam.getSkuId()); + if (!spuAndSkuResponse.isSuccess()) { + return ServerResponseEntity.transform(spuAndSkuResponse); + } + SkuVO sku = spuAndSkuResponse.getData().getSku(); + SpuVO spu = spuAndSkuResponse.getData().getSpu(); + // 拿到购物车的所有item + ShopCartItemVO shopCartItem = new ShopCartItemVO(); + shopCartItem.setCartItemId(0L); + shopCartItem.setSkuId(shopCartItemParam.getSkuId()); + shopCartItem.setCount(shopCartItemParam.getCount()); + shopCartItem.setSpuId(shopCartItemParam.getSpuId()); + shopCartItem.setSkuName(sku.getSkuName()); + shopCartItem.setSpuName(spu.getName()); + shopCartItem.setImgUrl(BooleanUtil.isTrue(spu.getHasSkuImg()) ? sku.getImgUrl() : spu.getMainImgUrl()); + shopCartItem.setSkuPriceFee(sku.getPriceFee()); + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + shopCartItem.setCreateTime(new Date()); + shopCartItem.setShopId(shopCartItemParam.getShopId()); + return ServerResponseEntity.success(Collections.singletonList(shopCartItem)); + } + + + /** + * 将参数转换成组装好的购物项 + * @param shopCartItems 订单参数 + * @return 组装好的购物项 + */ + public List conversionShopCart(List shopCartItems){ + + // 根据店铺ID划分item + Map> shopCartMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemVO::getShopId)); + + // 返回一个店铺的所有信息 + List shopCarts = Lists.newArrayList(); + for (Long shopId : shopCartMap.keySet()) { + // 构建每个店铺的购物车信息 + ShopCartVO shopCart = buildShopCart(shopId,shopCartMap.get(shopId)); + shopCart.setShopId(shopId); + shopCart.setshopCartItem(shopCartMap.get(shopId)); + // 店铺信息 + ServerResponseEntity shopNameResponse = shopDetailFeignClient.getShopNameByShopId(shopId); + if (!shopNameResponse.isSuccess()) { + throw new mall4cloudException(shopNameResponse.getMsg()); + } + shopCart.setShopName(shopNameResponse.getData()); + shopCarts.add(shopCart); + } + return shopCarts; + } + + + private ShopCartVO buildShopCart(Long shopId, List shopCartItems) { + ShopCartVO shopCart = new ShopCartVO(); + shopCart.setShopId(shopId); + long total = 0L; + int totalCount = 0; + for (ShopCartItemVO shopCartItem : shopCartItems) { + total += shopCartItem.getTotalAmount(); + totalCount += shopCartItem.getCount(); + } + shopCart.setTotal(total); + shopCart.setTotalCount(totalCount); + return shopCart; + } + + +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrCategoryVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrCategoryVO.java new file mode 100644 index 00000000..51c2e827 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrCategoryVO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 属性与属性分组关联信息VO + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +public class AttrCategoryVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性与分类关联id") + private Long attrCategoryId; + + @ApiModelProperty("分类id") + private Long categoryId; + + @ApiModelProperty("属性id") + private Long attrId; + + public Long getAttrCategoryId() { + return attrCategoryId; + } + + public void setAttrCategoryId(Long attrCategoryId) { + this.attrCategoryId = attrCategoryId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + @Override + public String toString() { + return "AttrCategoryVO{" + + "attrCategoryId=" + attrCategoryId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",categoryId=" + categoryId + + ",attrId=" + attrId + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrVO.java new file mode 100644 index 00000000..ec22f62f --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrVO.java @@ -0,0 +1,120 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * 属性信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +public class AttrVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("attr id") + private Long attrId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("属性名称") + private String name; + + @ApiModelProperty("属性描述") + private String desc; + + @ApiModelProperty("作为搜索参数 0:不需要,1:需要") + private Integer searchType; + + @ApiModelProperty("属性类型 0:销售属性,1:基本属性") + private Integer attrType; + + @ApiModelProperty("属性值列表") + private List attrValues; + + @ApiModelProperty("分类列表") + private List categories; + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Integer getAttrType() { + return attrType; + } + + public void setAttrType(Integer attrType) { + this.attrType = attrType; + } + + public Integer getSearchType() { + return searchType; + } + + public void setSearchType(Integer searchType) { + this.searchType = searchType; + } + + public List getAttrValues() { + return attrValues; + } + + public void setAttrValues(List attrValues) { + this.attrValues = attrValues; + } + + public List getCategories() { + return categories; + } + + public void setCategories(List categories) { + this.categories = categories; + } + + @Override + public String toString() { + return "AttrVO{" + + "attrId=" + attrId + + ", shopId=" + shopId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", attrType=" + attrType + + ", searchType=" + searchType + + ", attrValues=" + attrValues + + ", categorys=" + categories + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrValueVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrValueVO.java new file mode 100644 index 00000000..d7c40eec --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/AttrValueVO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 属性值信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class AttrValueVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性id") + private Long attrValueId; + + @ApiModelProperty("属性ID") + private Long attrId; + + @ApiModelProperty("属性值") + private String value; + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "AttrValueVO{" + + "attrValueId=" + attrValueId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",attrId=" + attrId + + ",value=" + value + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/BrandVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/BrandVO.java new file mode 100644 index 00000000..b98c91fe --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/BrandVO.java @@ -0,0 +1,123 @@ +package com.mall4j.cloud.api.product.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 品牌信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class BrandVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("brand_id") + private Long brandId; + + @ApiModelProperty("品牌名称") + private String name; + + @ApiModelProperty("品牌描述") + private String desc; + + @ApiModelProperty("品牌logo图片") + @JsonSerialize(using = ImgJsonSerializer.class) + private String imgUrl; + + @ApiModelProperty("检索首字母") + private String firstLetter; + + @ApiModelProperty("排序") + private Integer seq; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("分类") + private List categories; + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public String getFirstLetter() { + return firstLetter; + } + + public void setFirstLetter(String firstLetter) { + this.firstLetter = firstLetter; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public List getCategories() { + return categories; + } + + public void setCategories(List categories) { + this.categories = categories; + } + + @Override + public String toString() { + return "BrandVO{" + + "brandId=" + brandId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", firstLetter='" + firstLetter + '\'' + + ", seq=" + seq + + ", status=" + status + + ", categories=" + categories + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/CategoryVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/CategoryVO.java new file mode 100644 index 00000000..76123d30 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/CategoryVO.java @@ -0,0 +1,181 @@ +package com.mall4j.cloud.api.product.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 分类信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class CategoryVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("分类id") + private Long categoryId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("父ID") + private Long parentId; + + @ApiModelProperty("分类名称") + private String name; + + @ApiModelProperty("分类描述") + private String desc; + + @ApiModelProperty("分类地址{parent_id}-{child_id},...") + private String path; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @JsonSerialize(using = ImgJsonSerializer.class) + @ApiModelProperty("分类图标") + private String icon; + + @JsonSerialize(using = ImgJsonSerializer.class) + @ApiModelProperty("分类的显示图片") + private String imgUrl; + + @ApiModelProperty("分类层级 从0开始") + private Integer level; + + @ApiModelProperty("排序") + private Integer seq; + + @ApiModelProperty("上级分类名称") + private List pathNames; + + @ApiModelProperty("子分类列表") + private List categories; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public List getPathNames() { + return pathNames; + } + + public void setPathNames(List pathNames) { + this.pathNames = pathNames; + } + + public List getCategories() { + return categories; + } + + public void setCategories(List categories) { + this.categories = categories; + } + + @Override + public String toString() { + return "CategoryVO{" + + "categoryId=" + categoryId + + ", shopId=" + shopId + + ", parentId=" + parentId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", path='" + path + '\'' + + ", status=" + status + + ", icon='" + icon + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", level=" + level + + ", seq=" + seq + + ", pathNames=" + pathNames + + ", categories=" + categories + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SkuVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SkuVO.java new file mode 100644 index 00000000..9bb0385f --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SkuVO.java @@ -0,0 +1,190 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.math.BigDecimal; +import java.util.List; + +/** + * sku信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SkuVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性id") + private Long skuId; + + @ApiModelProperty("SPU id") + private Long spuId; + + @ApiModelProperty("多个销售属性值id逗号分隔") + private String attrs; + + @ApiModelProperty("sku名称") + private String skuName; + + @ApiModelProperty("banner图片") + private String imgUrl; + + @ApiModelProperty("售价,整数方式保存") + private Long priceFee; + + @ApiModelProperty("市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("库存") + private Integer stock; + + @ApiModelProperty("商品编码") + private String partyCode; + + @ApiModelProperty("商品条形码") + private String modelId; + + @ApiModelProperty("商品重量") + private BigDecimal weight; + + @ApiModelProperty("商品体积") + private BigDecimal volume; + + @ApiModelProperty("当前sku规格列表") + private List spuSkuAttrValues; + + public String getPartyCode() { + return partyCode; + } + + public void setPartyCode(String partyCode) { + this.partyCode = partyCode; + } + + public String getModelId() { + return modelId; + } + + public void setModelId(String modelId) { + this.modelId = modelId; + } + + public BigDecimal getWeight() { + return weight; + } + + public void setWeight(BigDecimal weight) { + this.weight = weight; + } + + public BigDecimal getVolume() { + return volume; + } + + public void setVolume(BigDecimal volume) { + this.volume = volume; + } + + public List getSpuSkuAttrValues() { + return spuSkuAttrValues; + } + + public void setSpuSkuAttrValues(List spuSkuAttrValues) { + this.spuSkuAttrValues = spuSkuAttrValues; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Long getSkuId() { + return skuId; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getAttrs() { + return attrs; + } + + public void setAttrs(String attrs) { + this.attrs = attrs; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + @Override + public String toString() { + return "SkuVO{" + + "skuId=" + skuId + + ", spuId=" + spuId + + ", attrs='" + attrs + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", status=" + status + + ", stock=" + stock + + ", spuSkuAttrValues=" + spuSkuAttrValues + + ", partyCode='" + partyCode + '\'' + + ", modelId='" + modelId + '\'' + + ", weight=" + weight + + ", volume=" + volume + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAndSkuVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAndSkuVO.java new file mode 100644 index 00000000..5aca53c6 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAndSkuVO.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.api.product.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +public class SpuAndSkuVO { + + @ApiModelProperty("spu信息") + private SpuVO spu; + + @ApiModelProperty("sku信息") + private SkuVO sku; + + public SpuVO getSpu() { + return spu; + } + + public void setSpu(SpuVO spu) { + this.spu = spu; + } + + public SkuVO getSku() { + return sku; + } + + public void setSku(SkuVO sku) { + this.sku = sku; + } + + @Override + public String toString() { + return "SpuAndSkuVO{" + + "spu=" + spu + + ", sku=" + sku + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAttrValueVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAttrValueVO.java new file mode 100644 index 00000000..155771fc --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuAttrValueVO.java @@ -0,0 +1,106 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品规格属性关联信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuAttrValueVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品属性值关联信息id") + private Long spuAttrValueId; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("规格属性id") + private Long attrId; + + @ApiModelProperty("规格属性名称") + private String attrName; + + @ApiModelProperty("规格属性值id") + private Long attrValueId; + + @ApiModelProperty("规格属性值名称") + private String attrValueName; + + @ApiModelProperty("搜索类型 0:不需要,1:需要") + private Integer searchType; + + public Long getSpuAttrValueId() { + return spuAttrValueId; + } + + public void setSpuAttrValueId(Long spuAttrValueId) { + this.spuAttrValueId = spuAttrValueId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + public Integer getSearchType() { + return searchType; + } + + public void setSearchType(Integer searchType) { + this.searchType = searchType; + } + + @Override + public String toString() { + return "SpuAttrValueVO{" + + "spuAttrValueId=" + spuAttrValueId + + ", spuId=" + spuId + + ", attrId=" + attrId + + ", attrName='" + attrName + '\'' + + ", attrValueId=" + attrValueId + + ", attrValueName='" + attrValueName + '\'' + + ", searchType=" + searchType + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuSkuAttrValueVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuSkuAttrValueVO.java new file mode 100644 index 00000000..46f1053e --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuSkuAttrValueVO.java @@ -0,0 +1,118 @@ +package com.mall4j.cloud.api.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品sku销售属性关联信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuSkuAttrValueVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品sku销售属性关联信息id") + private Integer spuSkuAttrId; + + @ApiModelProperty("SPU ID") + private Long spuId; + + @ApiModelProperty("SKU ID") + private Long skuId; + + @ApiModelProperty("销售属性ID") + private Integer attrId; + + @ApiModelProperty("销售属性名称") + private String attrName; + + @ApiModelProperty("销售属性值ID") + private Integer attrValueId; + + @ApiModelProperty("销售属性值") + private String attrValueName; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + public Integer getSpuSkuAttrId() { + return spuSkuAttrId; + } + + public void setSpuSkuAttrId(Integer spuSkuAttrId) { + this.spuSkuAttrId = spuSkuAttrId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Integer getAttrId() { + return attrId; + } + + public void setAttrId(Integer attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Integer getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Integer attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SpuSkuAttrValueVO{" + + "spuSkuAttrId=" + spuSkuAttrId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", attrId=" + attrId + + ", attrName='" + attrName + '\'' + + ", attrValueId=" + attrValueId + + ", attrValueName='" + attrValueName + '\'' + + ", status=" + status + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuVO.java b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuVO.java new file mode 100644 index 00000000..48553a82 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-product/src/main/java/com/mall4j/cloud/api/product/vo/SpuVO.java @@ -0,0 +1,314 @@ +package com.mall4j.cloud.api.product.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * spu信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("spu id") + private Long spuId; + + @ApiModelProperty("品牌ID") + private Long brandId; + + @ApiModelProperty("分类ID") + private Long categoryId; + + @ApiModelProperty("店铺分类ID") + private Long shopCategoryId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("spu名称") + private String name; + + @ApiModelProperty("卖点") + private String sellingPoint; + + @ApiModelProperty("商品介绍主图") + @JsonSerialize(using = ImgJsonSerializer.class) + private String mainImgUrl; + + @ApiModelProperty("商品介绍主图 多个图片逗号分隔") + @JsonSerialize(using = ImgJsonSerializer.class) + private String imgUrls; + + @ApiModelProperty("售价,整数方式保存") + private Long priceFee; + + @ApiModelProperty("市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("sku是否含有图片 0无 1有") + private Integer hasSkuImg; + + @ApiModelProperty("商品详情") + private String detail; + + @ApiModelProperty("总库存") + private Integer totalStock; + + @ApiModelProperty("规格属性") + private List spuAttrValues; + + @ApiModelProperty("sku列表") + private List skus; + + @ApiModelProperty("序号") + private Integer seq; + + @ApiModelProperty("品牌信息") + private BrandVO brand; + + @ApiModelProperty("商品销量") + private Integer saleNum; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("分类信息") + private CategoryVO category; + + @ApiModelProperty("店铺分类信息") + private CategoryVO shopCategory; + + @ApiModelProperty("分组商品关联id") + private Long referenceId; + + public Long getReferenceId() { + return referenceId; + } + + public void setReferenceId(Long referenceId) { + this.referenceId = referenceId; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public List getSkus() { + return skus; + } + + public void setSkus(List skus) { + this.skus = skus; + } + + public List getSpuAttrValues() { + return spuAttrValues; + } + + public void setSpuAttrValues(List spuAttrValues) { + this.spuAttrValues = spuAttrValues; + } + + public Integer getTotalStock() { + return totalStock; + } + + public void setTotalStock(Integer totalStock) { + this.totalStock = totalStock; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopCategoryId() { + return shopCategoryId; + } + + public void setShopCategoryId(Long shopCategoryId) { + this.shopCategoryId = shopCategoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImgUrls() { + return imgUrls; + } + + public void setImgUrls(String imgUrls) { + this.imgUrls = imgUrls; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public Integer getHasSkuImg() { + return hasSkuImg; + } + + public void setHasSkuImg(Integer hasSkuImg) { + this.hasSkuImg = hasSkuImg; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public BrandVO getBrand() { + return brand; + } + + public void setBrand(BrandVO brand) { + this.brand = brand; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public CategoryVO getCategory() { + return category; + } + + public void setCategory(CategoryVO category) { + this.category = category; + } + + public CategoryVO getShopCategory() { + return shopCategory; + } + + public void setShopCategory(CategoryVO shopCategory) { + this.shopCategory = shopCategory; + } + + @Override + public String toString() { + return "SpuVO{" + + "spuId=" + spuId + + ", brandId=" + brandId + + ", categoryId=" + categoryId + + ", shopCategoryId=" + shopCategoryId + + ", shopId=" + shopId + + ", name='" + name + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", imgUrls='" + imgUrls + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", status=" + status + + ", hasSkuImg=" + hasSkuImg + + ", detail='" + detail + '\'' + + ", totalStock=" + totalStock + + ", spuAttrValues=" + spuAttrValues + + ", skus=" + skus + + ", seq=" + seq + + ", brand=" + brand + + ", saleNum=" + saleNum + + ", shopName='" + shopName + '\'' + + ", category=" + category + + ", shopCategory=" + shopCategory + + ", referenceId=" + referenceId + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/pom.xml b/mall4cloud-api/mall4cloud-api-rbac/pom.xml new file mode 100644 index 00000000..b68f6481 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-rbac + jar + 用户角色权限对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/bo/UriPermissionBO.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/bo/UriPermissionBO.java new file mode 100644 index 00000000..bace4432 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/bo/UriPermissionBO.java @@ -0,0 +1,56 @@ +package com.mall4j.cloud.api.rbac.bo; + +/** + * uri权限bo + * + * @author FrozenWatermelon + * @date 2020/9/3 + */ +public class UriPermissionBO { + + /** + * 请求方法 1.GET 2.POST 3.PUT 4.DELETE + */ + private Integer method; + + /** + * uri + */ + private String uri; + + /** + * permission + */ + private String permission; + + public Integer getMethod() { + return method; + } + + public void setMethod(Integer method) { + this.method = method; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + @Override + public String toString() { + return "UriPermissionBO{" + "method=" + method + ", uri='" + uri + '\'' + ", permission='" + permission + '\'' + + '}'; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/constant/HttpMethodEnum.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/constant/HttpMethodEnum.java new file mode 100644 index 00000000..d1793950 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/constant/HttpMethodEnum.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.api.rbac.constant; + +import java.util.Objects; + +/** + * 请求方法 1.GET 2.POST 3.PUT 4.DELETE http方法枚举 + * @author FrozenWatermelon + * @date 2020/9/3 + */ +public enum HttpMethodEnum { + + /** + * GET + */ + GET(1, "GET"), + + /** + * POST + */ + POST(2, "POST"), + + /** + * PUT + */ + PUT(3, "PUT"), + + /** + * DELETE + */ + DELETE(4, "DELETE"),; + + private final Integer value; + + private final String method; + + public Integer value() { + return value; + } + + public String method() { + return this.method; + } + + HttpMethodEnum(Integer value, String method) { + this.value = value; + this.method = method; + } + + public static HttpMethodEnum valueOf(Integer value) { + for (HttpMethodEnum httpMethodEnum : values()) { + if (Objects.equals(httpMethodEnum.value(), value)) { + return httpMethodEnum; + } + } + return null; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/CheckPermissionDTO.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/CheckPermissionDTO.java new file mode 100644 index 00000000..3bd3d8f8 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/CheckPermissionDTO.java @@ -0,0 +1,87 @@ +package com.mall4j.cloud.api.rbac.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020/9/2 + */ +public class CheckPermissionDTO { + + /** + * 用户id + */ + @NotNull(message = "userId not null") + private Long userId; + + /** + * 系统类型 + */ + @NotNull(message = "sysType not null") + private Integer sysType; + + /** + * uri + */ + @NotBlank(message = "uri not blank") + private String uri; + + /** + * 是否是管理员 + */ + @NotNull(message = "isAdmin not null") + private Integer isAdmin; + + /** + * 请求方法 1.GET 2.POST 3.PUT 4.DELETE + */ + private Integer method; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + public Integer getMethod() { + return method; + } + + public void setMethod(Integer method) { + this.method = method; + } + + @Override + public String toString() { + return "CheckPermissionDTO{" + "userId=" + userId + ", sysType=" + sysType + ", uri='" + uri + '\'' + + ", isAdmin=" + isAdmin + ", method=" + method + '}'; + } + +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/ClearUserPermissionsCacheDTO.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/ClearUserPermissionsCacheDTO.java new file mode 100644 index 00000000..82f6b283 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/ClearUserPermissionsCacheDTO.java @@ -0,0 +1,46 @@ +package com.mall4j.cloud.api.rbac.dto; + +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020/11/27 + */ +public class ClearUserPermissionsCacheDTO { + + /** + * 用户id + */ + @NotNull(message = "userId not null") + private Long userId; + + /** + * 系统类型 + */ + @NotNull(message = "sysType not null") + private Integer sysType; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + @Override + public String toString() { + return "ClearUserPermissionsCacheDTO{" + + "userId=" + userId + + ", sysType=" + sysType + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/UserRoleDTO.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/UserRoleDTO.java new file mode 100644 index 00000000..d6bfd5bf --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/dto/UserRoleDTO.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.api.rbac.dto; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/27 + */ +public class UserRoleDTO { + + /** + * 用户id + */ + @NotNull(message = "userId not null") + private Long userId; + + + /** + * 角色id列表 + */ + @NotEmpty(message = "userId not null") + private List roleIds; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + @Override + public String toString() { + return "UserRoleDTO{" + + "userId=" + userId + + ", roleIds=" + roleIds + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/PermissionFeignClient.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/PermissionFeignClient.java new file mode 100644 index 00000000..548c5bde --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/PermissionFeignClient.java @@ -0,0 +1,45 @@ +package com.mall4j.cloud.api.rbac.feign; + +import com.mall4j.cloud.api.rbac.dto.ClearUserPermissionsCacheDTO; +import com.mall4j.cloud.common.constant.Auth; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * + * @author FrozenWatermelon + * @date 2020/09/02 + */ +@FeignClient(value = PermissionFeignClient.SERVICE_NAME,contextId = "permission") +public interface PermissionFeignClient { + + String SERVICE_NAME = "mall4cloud-rbac"; + + /** + * 校验是否有某个uri的权限 + * @param userId + * @param sysType + * @param uri + * @param isAdmin + * @param method + * @return 是否有某个uri的权限 + */ + @GetMapping(value = Auth.CHECK_RBAC_URI) + ServerResponseEntity checkPermission(@RequestParam("userId") Long userId,@RequestParam("sysType") Integer sysType, + @RequestParam("uri") String uri,@RequestParam("isAdmin") Integer isAdmin, + @RequestParam("method") Integer method); + + /** + * 清除用户权限缓存 + * @param clearUserPermissionsCacheDTO + * @return 是否调用成功 + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/permission/clearUserPermissionsCache") + ServerResponseEntity clearUserPermissionsCache(@RequestBody ClearUserPermissionsCacheDTO clearUserPermissionsCacheDTO); + +} diff --git a/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/UserRoleFeignClient.java b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/UserRoleFeignClient.java new file mode 100644 index 00000000..4330a6c3 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-rbac/src/main/java/com/mall4j/cloud/api/rbac/feign/UserRoleFeignClient.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.api.rbac.feign; + +import com.mall4j.cloud.api.rbac.dto.UserRoleDTO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/25 + */ +@FeignClient(value = "mall4cloud-rbac",contextId = "userRole") +public interface UserRoleFeignClient { + + + /** + * 保存用户角色关联信息 + * @param userRoleDTO 用户角色关联信息 + * @return void + */ + @PostMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/userRole/saveByUserIdAndSysType") + ServerResponseEntity saveByUserIdAndSysType(@RequestBody UserRoleDTO userRoleDTO); + + /** + * 更新用户角色关联信息 + * @param userRoleDTO 用户角色关联信息 + * @return void + */ + @PutMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/userRole/updateByUserIdAndSysType") + ServerResponseEntity updateByUserIdAndSysType(@RequestBody UserRoleDTO userRoleDTO); + + /** + * 删除用户角色关联信息 + * @param userId 用户id + * @return void + */ + @DeleteMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/userRole/deleteByUserIdAndSysType") + ServerResponseEntity deleteByUserIdAndSysType(@RequestParam("userId") Long userId); + + /** + * 获取用户角色关联信息 + * @param userId 用户id + * @return 用户角色关联ids + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/userRole/getRoleIds") + ServerResponseEntity> getRoleIds(@RequestParam("userId") Long userId); +} diff --git a/mall4cloud-api/mall4cloud-api-search/pom.xml b/mall4cloud-api/mall4cloud-api-search/pom.xml new file mode 100644 index 00000000..0dd0ec1b --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/pom.xml @@ -0,0 +1,24 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-search + jar + 搜索对内接口 + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + + \ No newline at end of file diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/EsPageDTO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/EsPageDTO.java new file mode 100644 index 00000000..09d1ff0a --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/EsPageDTO.java @@ -0,0 +1,143 @@ +package com.mall4j.cloud.api.dto; + + +import com.mall4j.cloud.common.util.PrincipalUtil; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +public class EsPageDTO{ + + public static final String ASC = "ASC"; + + public static final String DESC = "DESC"; + + /** + * 最大分页大小,如果分页大小大于500,则用500作为分页的大小。防止有人直接传入一个较大的数,导致服务器内存溢出宕机 + */ + public static final Integer MAX_PAGE_SIZE = 500; + + /** + * 当前页 + */ + @NotNull(message = "pageNum 不能为空") + @ApiModelProperty(value = "当前页", required = true) + private Integer pageNum; + + @NotNull(message = "pageSize 不能为空") + @ApiModelProperty(value = "每页大小", required = true) + private Integer pageSize; + + @ApiModelProperty(value = "排序字段数组,用逗号分割") + private String[] columns; + + @ApiModelProperty(value = "排序字段方式,用逗号分割,ASC正序,DESC倒序") + private String[] orders; + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + if (pageSize > MAX_PAGE_SIZE) { + this.pageSize = MAX_PAGE_SIZE; + return; + } + this.pageSize = pageSize; + } + + public String getOrderBy() { + return order(this.columns, this.orders); + } + + public String[] getColumns() { + return columns; + } + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public String[] getOrders() { + return orders; + } + + public void setOrders(String[] orders) { + this.orders = orders; + } + + public static String order(String[] columns, String[] orders) { + + if (columns == null || columns.length == 0) { + return ""; + } + + StringBuilder stringBuilder = new StringBuilder(); + + for (int x = 0; x < columns.length; x++) { + + String column = columns[x]; + String order; + + if (orders != null && orders.length > x) { + order = orders[x].toUpperCase(); + if (!(order.equals(ASC) || order.equals(DESC))) { + throw new IllegalArgumentException("非法的排序策略:" + column); + } + }else { + order = ASC; + } + + // 判断列名称的合法性,防止SQL注入。只能是【字母,数字,下划线】 + if (PrincipalUtil.isField(column)) { + throw new IllegalArgumentException("非法的排序字段名称:" + column); + } + + // 驼峰转换为下划线 + column = humpConversionUnderscore(column); + + if (x != 0) { + stringBuilder.append(", "); + } + stringBuilder.append("`").append(column).append("` ").append(order); + } + return stringBuilder.toString(); + } + + public static String humpConversionUnderscore(String value) { + StringBuilder stringBuilder = new StringBuilder(); + char[] chars = value.toCharArray(); + for (char character : chars) { + if (Character.isUpperCase(character)) { + stringBuilder.append("_"); + character = Character.toLowerCase(character); + } + stringBuilder.append(character); + } + return stringBuilder.toString(); + } + + + + @Override + public String toString() { + return "EsPageDTO{" + + "pageNum=" + pageNum + + ", pageSize=" + pageSize + + ", columns=" + Arrays.toString(columns) + + ", orders=" + Arrays.toString(orders) + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/ProductSearchDTO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/ProductSearchDTO.java new file mode 100644 index 00000000..3bebf7d3 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/dto/ProductSearchDTO.java @@ -0,0 +1,325 @@ +package com.mall4j.cloud.api.dto; + + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; +import java.util.Map; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +public class ProductSearchDTO { + + @ApiModelProperty(value = "页面传递过来的全文匹配关键字") + private String keyword; + + @ApiModelProperty(value = "品牌id,可以多选") + private String brandIds; + + @ApiModelProperty(value = "商家一级分类id") + private Long shopPrimaryCategoryId; + + @ApiModelProperty(value = "商家二级分类id") + private Long shopSecondaryCategoryId; + + @ApiModelProperty(value = "平台一级分类id") + private Long primaryCategoryId; + + @ApiModelProperty(value = "平台三级分类id") + private Long categoryId; + + @ApiModelProperty(value = "排序:1新品,2销量倒序,3销量正序,4商品价格倒序,5商品价格正序,6评论(暂无评论)") + private Integer sort; + + @ApiModelProperty(value = "自营店 1:自营店 2:非自营店") + private Integer selfShop; + + @ApiModelProperty(value = "是否显示有货") + private Integer hasStock; + + @ApiModelProperty(value = "价格区间查询-最低价") + private Long minPrice; + + @ApiModelProperty(value = "价格区间查询-最高价") + private Long maxPrice; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @ApiModelProperty(value = "属性值ids(属性之间用^拼接;属性于属性值id用_拼接;多个属性值id间用,拼接)") + private String attrIds; + + @ApiModelProperty(value = "商品状态") + private Integer spuStatus; + + @ApiModelProperty(value = "属性值ids(多个id间使用 , 分隔)") + private String attrValueIds; + + @ApiModelProperty(value = "spuId列表") + private List spuIds; + + @ApiModelProperty(value = "销量区间查询-最低销量") + private Long minSaleNum; + + @ApiModelProperty(value = "销量区间查询-最高销量") + private Long maxSaleNum; + + @ApiModelProperty(value = "商品编码列表(逗号分隔)") + private String partyCodes; + + @ApiModelProperty(value = "商品条形码列表(逗号分隔)") + private String modelIds; + + @ApiModelProperty(value = "0.全部 1.销售中 2.已售罄 3.已下架") + private Integer dataType; + + @ApiModelProperty(value = "当前页") + private Integer pageNum; + + @ApiModelProperty(value = "每页大小") + private Integer pageSize; + + /** + * 对应SearchTypeEnum + * 搜索类型 1:用户端搜索 2:店铺spu列表 3.平台spu管理列表 + */ + private Integer searchType; + + /** + * 搜索属性信息 + */ + private Map> attrMap; + + public Map> getAttrMap() { + return attrMap; + } + + public void setAttrMap(Map> attrMap) { + this.attrMap = attrMap; + } + + public String getAttrIds() { + return attrIds; + } + + public void setAttrIds(String attrIds) { + this.attrIds = attrIds; + } + + public Long getMinSaleNum() { + return minSaleNum; + } + + public void setMinSaleNum(Long minSaleNum) { + this.minSaleNum = minSaleNum; + } + + public Long getMaxSaleNum() { + return maxSaleNum; + } + + public void setMaxSaleNum(Long maxSaleNum) { + this.maxSaleNum = maxSaleNum; + } + + public String getPartyCodes() { + return partyCodes; + } + + public void setPartyCodes(String partyCodes) { + this.partyCodes = partyCodes; + } + + public String getModelIds() { + return modelIds; + } + + public void setModelIds(String modelIds) { + this.modelIds = modelIds; + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public String getBrandIds() { + return brandIds; + } + + public void setBrandIds(String brandIds) { + this.brandIds = brandIds; + } + + public Long getShopPrimaryCategoryId() { + return shopPrimaryCategoryId; + } + + public void setShopPrimaryCategoryId(Long shopPrimaryCategoryId) { + this.shopPrimaryCategoryId = shopPrimaryCategoryId; + } + + public Long getShopSecondaryCategoryId() { + return shopSecondaryCategoryId; + } + + public void setShopSecondaryCategoryId(Long shopSecondaryCategoryId) { + this.shopSecondaryCategoryId = shopSecondaryCategoryId; + } + + public Long getPrimaryCategoryId() { + return primaryCategoryId; + } + + public void setPrimaryCategoryId(Long primaryCategoryId) { + this.primaryCategoryId = primaryCategoryId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + public Integer getSelfShop() { + return selfShop; + } + + public void setSelfShop(Integer selfShop) { + this.selfShop = selfShop; + } + + public Integer getHasStock() { + return hasStock; + } + + public void setHasStock(Integer hasStock) { + this.hasStock = hasStock; + } + + public Long getMinPrice() { + return minPrice; + } + + public void setMinPrice(Long minPrice) { + this.minPrice = minPrice; + } + + public Long getMaxPrice() { + return maxPrice; + } + + public void setMaxPrice(Long maxPrice) { + this.maxPrice = maxPrice; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSpuStatus() { + return spuStatus; + } + + public void setSpuStatus(Integer spuStatus) { + this.spuStatus = spuStatus; + } + + public String getAttrValueIds() { + return attrValueIds; + } + + public void setAttrValueIds(String attrValueIds) { + this.attrValueIds = attrValueIds; + } + + public List getSpuIds() { + return spuIds; + } + + public void setSpuIds(List spuIds) { + this.spuIds = spuIds; + } + + public Integer getDataType() { + return dataType; + } + + public void setDataType(Integer dataType) { + this.dataType = dataType; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Integer getSearchType() { + return searchType; + } + + public void setSearchType(Integer searchType) { + this.searchType = searchType; + } + + @Override + public String toString() { + return "ProductSearchDTO{" + + "keyword='" + keyword + '\'' + + ", brandIds='" + brandIds + '\'' + + ", shopPrimaryCategoryId=" + shopPrimaryCategoryId + + ", shopSecondaryCategoryId=" + shopSecondaryCategoryId + + ", primaryCategoryId=" + primaryCategoryId + + ", categoryId=" + categoryId + + ", sort=" + sort + + ", selfShop=" + selfShop + + ", hasStock=" + hasStock + + ", minPrice=" + minPrice + + ", maxPrice=" + maxPrice + + ", shopId=" + shopId + + ", attrIds='" + attrIds + '\'' + + ", spuStatus=" + spuStatus + + ", attrValueIds='" + attrValueIds + '\'' + + ", spuIds=" + spuIds + + ", minSaleNum=" + minSaleNum + + ", maxSaleNum=" + maxSaleNum + + ", partyCodes='" + partyCodes + '\'' + + ", modelIds='" + modelIds + '\'' + + ", dataType=" + dataType + + ", pageNum=" + pageNum + + ", pageSize=" + pageSize + + ", searchType=" + searchType + + ", attrMap=" + attrMap + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchOrderFeignClient.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchOrderFeignClient.java new file mode 100644 index 00000000..1c4b4170 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchOrderFeignClient.java @@ -0,0 +1,28 @@ +package com.mall4j.cloud.api.feign; + +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.EsOrderVO; +import com.mall4j.cloud.common.dto.OrderSearchDTO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +/** + * 订单搜索 + * @author FrozenWatermelon + * @date 2021/02/05 + */ +@FeignClient(value = "mall4cloud-search",contextId = "searchOrder") +public interface SearchOrderFeignClient { + + + /** + * 订单搜索 + * @param orderSearch 订单搜索参数 + * @return 订单列表 + */ + @PutMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/searchOrder/getOrderPage") + ServerResponseEntity> getOrderPage(@RequestBody OrderSearchDTO orderSearch); + +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchSpuFeignClient.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchSpuFeignClient.java new file mode 100644 index 00000000..cfdb19af --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/feign/SearchSpuFeignClient.java @@ -0,0 +1,59 @@ +package com.mall4j.cloud.api.feign; + +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.ProductSearchVO; +import com.mall4j.cloud.api.vo.search.SpuSearchVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * 商品搜索feign连接 + * @author YXF + * @date 2020/12/07 + */ +@FeignClient(value = "mall4cloud-search",contextId = "searchSpu") +public interface SearchSpuFeignClient { + + /** + * 商品搜索 + * @param pageDTO + * @param productSearchDTO + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/searchSpu/search") + ServerResponseEntity> search(@RequestParam("pageDTO") EsPageDTO pageDTO, @RequestParam("productSearchDTO") ProductSearchDTO productSearchDTO); + /** + * 根据spuId列表, 获取spu列表信息 + * @param spuIds 商品id列表 + * @return 商品列表 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/searchSpu/getSpusBySpuIds") + ServerResponseEntity> getSpusBySpuIds(@RequestParam("spuIds") List spuIds); + + /** + * 根据店铺,获取商品分页信息 + * @param pageNum 分页数 + * @param pageSize 每页大小 + * @param shopId 店铺id + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/searchSpu/spuPage") + ServerResponseEntity> spuPage(@RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize, @RequestParam("shopId") Long shopId); + + /** + * 根据店铺id列表获取每个店铺的spu列表 + * @param shopIds 店铺id列表 + * @param size 每个店铺返回的商品数量 + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/searchSpu/limitSizeListByShopIds") + ServerResponseEntity> limitSizeListByShopIds(@RequestParam("shopIds") List shopIds, @RequestParam("size") Integer size); + +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/EsPageVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/EsPageVO.java new file mode 100644 index 00000000..9ba2f24c --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/EsPageVO.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.api.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +public class EsPageVO { + + @ApiModelProperty("总页数") + private Integer pages; + + @ApiModelProperty("总条目数") + private Long total; + + @ApiModelProperty("结果集") + private List list; + + public Integer getPages() { + return pages; + } + + public void setPages(Integer pages) { + this.pages = pages; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + @Override + public String toString() { + return "EsPageVO{" + + ", pages=" + pages + + ", total=" + total + + ", list=" + list + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrSearchVO.java new file mode 100644 index 00000000..2dd8f018 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrSearchVO.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public class AttrSearchVO { + + @ApiModelProperty(value = "规格id") + private Long attrId; + + @ApiModelProperty(value = "规格名") + private String attrName; + + @ApiModelProperty(value = "规格值列表") + private List attrValues; + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public List getAttrValues() { + return attrValues; + } + + public void setAttrValues(List attrValues) { + this.attrValues = attrValues; + } + + @Override + public String toString() { + return "AttrVO{" + + "attrId=" + attrId + + ", attrName='" + attrName + '\'' + + ", attrValues=" + attrValues + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrValueSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrValueSearchVO.java new file mode 100644 index 00000000..222fb70d --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/AttrValueSearchVO.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public class AttrValueSearchVO { + + @ApiModelProperty(value = "规格值id") + private Long attrValueId; + + @ApiModelProperty(value = "规格值名称") + private String attrValueName; + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + @Override + public String toString() { + return "AttrValueVO{" + + "attrValueId=" + attrValueId + + ", attrValueName='" + attrValueName + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/BrandSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/BrandSearchVO.java new file mode 100644 index 00000000..adee34af --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/BrandSearchVO.java @@ -0,0 +1,52 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public class BrandSearchVO { + + @ApiModelProperty(value = "品牌名称") + private String brandName; + + @ApiModelProperty(value = "品牌id") + private Long brandId; + + @ApiModelProperty(value = "品牌图片") + private String brandImg; + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + this.brandName = brandName; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getBrandImg() { + return brandImg; + } + + public void setBrandImg(String brandImg) { + this.brandImg = brandImg; + } + + @Override + public String toString() { + return "BrandVO{" + + "brandName='" + brandName + '\'' + + ", brandId=" + brandId + + ", brandImg='" + brandImg + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/CategorySearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/CategorySearchVO.java new file mode 100644 index 00000000..0cd3cd9e --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/CategorySearchVO.java @@ -0,0 +1,43 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 分类信息VO + * + * @author YXF + * @date 2020-1-05 15:37:24 + */ +public class CategorySearchVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("分类id") + private Long categoryId; + + @ApiModelProperty("分类名称") + private String name; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "CategorySearchVO{" + + "categoryId=" + categoryId + + ", name='" + name + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderItemVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderItemVO.java new file mode 100644 index 00000000..d20cd935 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderItemVO.java @@ -0,0 +1,187 @@ +package com.mall4j.cloud.api.vo.search; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * @author FrozenWatermelon + * @date 2021/2/5 + */ +public class EsOrderItemVO { + @ApiModelProperty(value = "商品图片", required = true) + @JsonSerialize(using = ImgJsonSerializer.class) + private String pic; + + @ApiModelProperty(value = "商品名称", required = true) + private String spuName; + + @ApiModelProperty(value = "商品数量", required = true) + private Integer count; + + @ApiModelProperty(value = "商品价格", required = true) + private Long price; + + @ApiModelProperty(value = "skuId", required = true) + private Long skuId; + + @ApiModelProperty(value = "skuName", required = true) + private String skuName; + + @ApiModelProperty(value = "订单项id", required = true) + private Long orderItemId; + + @ApiModelProperty(value = "商品id", required = true) + private Long spuId; + + + /** + * 店铺id + */ + private Long shopId; + + /** + * 用户Id + */ + private Long userId; + + /** + * 单个orderItem的配送类型 3:无需快递 + */ + private Integer deliveryType; + + /** + * 加入购物车时间 + */ + private Date shopCartTime; + + /** + * 商品总金额 + */ + private Long spuTotalAmount; + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Date getShopCartTime() { + return shopCartTime; + } + + public void setShopCartTime(Date shopCartTime) { + this.shopCartTime = shopCartTime; + } + + public Long getSpuTotalAmount() { + return spuTotalAmount; + } + + public void setSpuTotalAmount(Long spuTotalAmount) { + this.spuTotalAmount = spuTotalAmount; + } + + @Override + public String toString() { + return "EsOrderItemVO{" + + "pic='" + pic + '\'' + + ", spuName='" + spuName + '\'' + + ", count=" + count + + ", price=" + price + + ", skuId=" + skuId + + ", skuName='" + skuName + '\'' + + ", orderItemId=" + orderItemId + + ", spuId=" + spuId + + ", shopId=" + shopId + + ", userId=" + userId + + ", deliveryType=" + deliveryType + + ", shopCartTime=" + shopCartTime + + ", spuTotalAmount=" + spuTotalAmount + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderVO.java new file mode 100644 index 00000000..09663de3 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/EsOrderVO.java @@ -0,0 +1,277 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2021/2/5 + */ +public class EsOrderVO { + + @ApiModelProperty(value = "订单项",required=true) + private List orderItems; + + @ApiModelProperty(value = "订单号",required=true) + private Long orderId; + + @ApiModelProperty(value = "总价",required=true) + private Long actualTotal; + + @ApiModelProperty(value = "订单状态",required=true) + private Integer status; + + @ApiModelProperty(value = "配送类型 3:无需快递",required=true) + private Integer deliveryType; + + @ApiModelProperty(value = "店铺名称",required=true) + private String shopName; + + @ApiModelProperty(value = "店铺id",required=true) + private Long shopId; + + @ApiModelProperty(value = "订单创建时间",required=true) + private Date createTime; + + @ApiModelProperty(value = "商品总数",required=true) + private Integer allCount; + + @ApiModelProperty(value = "收货人姓名") + private String consignee; + + @ApiModelProperty(value = "收货人手机号") + private String mobile; + + /** + * 用户订单地址Id + */ + private Long orderAddrId; + + /** + * 总值 + */ + private Long total; + + /** + * 订单关闭原因 1-超时未支付4-买家取消 15-已通过货到付款交易 + */ + private Integer closeType; + + /** + * 付款时间 + */ + private Date payTime; + + /** + * 发货时间 + */ + private Date deliveryTime; + + /** + * 完成时间 + */ + private Date finallyTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 是否已支付,1.已支付0.未支付 + */ + private Integer isPayed; + + /** + * 用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除 + */ + private Integer deleteStatus; + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getActualTotal() { + return actualTotal; + } + + public void setActualTotal(Long actualTotal) { + this.actualTotal = actualTotal; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Integer getDeleteStatus() { + return deleteStatus; + } + + public void setDeleteStatus(Integer deleteStatus) { + this.deleteStatus = deleteStatus; + } + + @Override + public String toString() { + return "EsOrderVO{" + + "orderItems=" + orderItems + + ", orderId=" + orderId + + ", actualTotal=" + actualTotal + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", shopName='" + shopName + '\'' + + ", shopId=" + shopId + + ", createTime=" + createTime + + ", allCount=" + allCount + + ", consignee='" + consignee + '\'' + + ", mobile='" + mobile + '\'' + + ", orderAddrId=" + orderAddrId + + ", total=" + total + + ", closeType=" + closeType + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", isPayed=" + isPayed + + ", deleteStatus=" + deleteStatus + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ProductSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ProductSearchVO.java new file mode 100644 index 00000000..0a86e41a --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ProductSearchVO.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +public class ProductSearchVO { + + + @ApiModelProperty(value = "店铺信息") + private ShopInfoSearchVO shopInfo; + + @ApiModelProperty(value = "规格属性列表") + private List attrs; + + @ApiModelProperty(value = "品牌列表信息") + private List brands; + + @ApiModelProperty(value = "spu列表信息") + private List spus; + + @ApiModelProperty(value = "分类列表信息") + private List categorys; + + public ShopInfoSearchVO getShopInfo() { + return shopInfo; + } + + public void setShopInfo(ShopInfoSearchVO shopInfo) { + this.shopInfo = shopInfo; + } + + public List getAttrs() { + return attrs; + } + + public void setAttrs(List attrs) { + this.attrs = attrs; + } + + public List getBrands() { + return brands; + } + + public void setBrands(List brands) { + this.brands = brands; + } + + public List getSpus() { + return spus; + } + + public void setSpus(List spus) { + this.spus = spus; + } + + public List getCategorys() { + return categorys; + } + + public void setCategorys(List categorys) { + this.categorys = categorys; + } + + @Override + public String toString() { + return "ProductSearchVO{" + + "shopInfo=" + shopInfo + + ", attrs=" + attrs + + ", brands=" + brands + + ", spus=" + spus + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ShopInfoSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ShopInfoSearchVO.java new file mode 100644 index 00000000..763d50b9 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/ShopInfoSearchVO.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.api.vo.search; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public class ShopInfoSearchVO { + + @ApiModelProperty(value = "店铺名称 搜索华为的时候,可以把华为的旗舰店搜索出来") + private String shopName; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @ApiModelProperty(value = "店铺logo") + @JsonSerialize(using =ImgJsonSerializer.class) + private String shopLogo; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + @Override + public String toString() { + return "ShopInfoSearchVO{" + + "shopName='" + shopName + '\'' + + ", shopId=" + shopId + + ", type=" + type + + ", shopLogo='" + shopLogo + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/SpuSearchVO.java b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/SpuSearchVO.java new file mode 100644 index 00000000..a47e264b --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-search/src/main/java/com/mall4j/cloud/api/vo/search/SpuSearchVO.java @@ -0,0 +1,123 @@ +package com.mall4j.cloud.api.vo.search; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public class SpuSearchVO { + + @ApiModelProperty(value = "商品id") + private Long spuId; + + @ApiModelProperty(value = "商品名称") + private String spuName; + + @ApiModelProperty(value = "卖点") + private String sellingPoint; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty(value = "商品售价") + private Long priceFee; + + @ApiModelProperty(value = "市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty(value = "是否有库存") + private Boolean hasStock; + + @ApiModelProperty(value = "销量") + private Integer saleNum; + + @ApiModelProperty("商品介绍主图") + private String mainImgUrl; + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Boolean getHasStock() { + return hasStock; + } + + public void setHasStock(Boolean hasStock) { + this.hasStock = hasStock; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + @Override + public String toString() { + return "SpuVO{" + + "spuId=" + spuId + + ", spuName='" + spuName + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", hasStock=" + hasStock + + ", saleNum=" + saleNum + + ", mainImgUrl='" + mainImgUrl + '\'' + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-user/pom.xml b/mall4cloud-api/mall4cloud-api-user/pom.xml new file mode 100644 index 00000000..dd0a13eb --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-user/pom.xml @@ -0,0 +1,23 @@ + + + + mall4cloud-api + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api-user + jar + 用户对内接口 + + + com.mall4j.cloud + mall4cloud-common-order + ${project.version} + + + + diff --git a/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserAddrFeignClient.java b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserAddrFeignClient.java new file mode 100644 index 00000000..1733a80f --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserAddrFeignClient.java @@ -0,0 +1,27 @@ +package com.mall4j.cloud.api.user.feign; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 用户地址feign连接 + * @author FrozenWatermelon + * @date 2020/12/07 + */ +@FeignClient(value = "mall4cloud-user",contextId = "userAddr") +public interface UserAddrFeignClient { + + + /** + * 根据地址id获取用户地址信息 + * @param addrId 地址id + * @return 用户地址信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/userAddr/getUserAddrByAddrId") + ServerResponseEntity getUserAddrByAddrId(@RequestParam("addrId") Long addrId); + +} diff --git a/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserFeignClient.java b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserFeignClient.java new file mode 100644 index 00000000..b36cbdf0 --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/feign/UserFeignClient.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.api.user.feign; + +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * 用户信息feign连接 + * @author FrozenWatermelon + * @date 2020/12/07 + */ +@FeignClient(value = "mall4cloud-user",contextId = "user") +public interface UserFeignClient { + + /** + * 根据用户id列表,获取用户信息 + * @param userIds 用户id列表 + * @return 用户列表信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/user/getUserByUserIds") + ServerResponseEntity> getUserByUserIds(@RequestParam("userId") List userIds); + + /** + * 根据用户id获取用户详细信息 + * @param userId 用户id + * @return 用户详细信息 + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/user/getUserAndOpenIdsByUserId") + ServerResponseEntity getUserAndOpenIdsByUserId(@RequestParam("userId") Long userId); + + /** + * 获取用户数据 + * @param userId + * @return + */ + @GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/user/getUserData") + ServerResponseEntity getUserData(Long userId); + +} diff --git a/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/AreaVO.java b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/AreaVO.java new file mode 100644 index 00000000..cc549bcb --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/AreaVO.java @@ -0,0 +1,109 @@ +package com.mall4j.cloud.api.user.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 省市区地区信息VO + * + * @author YXF + * @date 2020-11-25 15:16:14 + */ +public class AreaVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long areaId; + + @ApiModelProperty("地址") + private String areaName; + + @ApiModelProperty("上级地址") + private Long parentId; + + @ApiModelProperty("等级(从1开始)") + private Integer level; + + private Integer check; + + /** + * 下级地址集合 + */ + private List areas; + + /** + * 下级地址的areaId + */ + private List areaIds; + + public Long getAreaId() { + return areaId; + } + + public Integer getCheck() { + return check; + } + + public void setCheck(Integer check) { + this.check = check; + } + + public List getAreas() { + return areas; + } + + public void setAreas(List areas) { + this.areas = areas; + } + + public List getAreaIds() { + return areaIds; + } + + public void setAreaIds(List areaIds) { + this.areaIds = areaIds; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getAreaName() { + return areaName; + } + + public void setAreaName(String areaName) { + this.areaName = areaName; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + @Override + public String toString() { + return "AreaDTO{" + + "areaId=" + areaId + + ", areaName='" + areaName + '\'' + + ", parentId=" + parentId + + ", level=" + level + + ", check=" + check + + ", areas=" + areas + + ", areaIds=" + areaIds + + '}'; + } +} diff --git a/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/UserApiVO.java b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/UserApiVO.java new file mode 100644 index 00000000..a587ceff --- /dev/null +++ b/mall4cloud-api/mall4cloud-api-user/src/main/java/com/mall4j/cloud/api/user/vo/UserApiVO.java @@ -0,0 +1,90 @@ +package com.mall4j.cloud.api.user.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 用户表VO + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public class UserApiVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long userId; + + @ApiModelProperty("用户昵称") + private String nickName; + + @ApiModelProperty("头像图片路径") + @JsonSerialize(using = ImgJsonSerializer.class) + private String pic; + + @ApiModelProperty("状态 1 正常 0 无效") + private Integer status; + + @ApiModelProperty("是否创建过店铺") + + /** + * openId list + */ + private List bizUserIdList; + + public List getBizUserIdList() { + return bizUserIdList; + } + + public void setBizUserIdList(List bizUserIdList) { + this.bizUserIdList = bizUserIdList; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "UserApiVO{" + + "userId=" + userId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",nickName=" + nickName + + ",pic=" + pic + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-api/pom.xml b/mall4cloud-api/pom.xml new file mode 100644 index 00000000..a9765464 --- /dev/null +++ b/mall4cloud-api/pom.xml @@ -0,0 +1,29 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-api + pom + + mall4cloud 内网接口 + + mall4cloud-api-auth + mall4cloud-api-biz + mall4cloud-api-rbac + mall4cloud-api-leaf + mall4cloud-api-product + mall4cloud-api-multishop + mall4cloud-api-user + mall4cloud-api-order + mall4cloud-api-search + mall4cloud-api-platform + + + diff --git a/mall4cloud-auth/pom.xml b/mall4cloud-auth/pom.xml new file mode 100644 index 00000000..ec1f0394 --- /dev/null +++ b/mall4cloud-auth/pom.xml @@ -0,0 +1,68 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + mall4cloud-auth + jar + + mall4cloud 授权校验模块 + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-cache + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.github.anji-plus + captcha + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-biz + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/AuthApplication.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/AuthApplication.java new file mode 100644 index 00000000..c3226c52 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/AuthApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.auth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/7/8 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class AuthApplication { + + public static void main(String[] args) { + SpringApplication.run(AuthApplication.class, args); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/adapter/CaptchaCacheServiceRedisImpl.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/adapter/CaptchaCacheServiceRedisImpl.java new file mode 100644 index 00000000..7d6d0f66 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/adapter/CaptchaCacheServiceRedisImpl.java @@ -0,0 +1,39 @@ +package com.mall4j.cloud.auth.adapter; + +import com.anji.captcha.service.CaptchaCacheService; +import com.mall4j.cloud.common.cache.util.RedisUtil; + +/** + * 适配验证码在redis的存储 + * + * @author FrozenWatermelon + * @date 2020/8/12 + */ +public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService { + + @Override + public void set(String key, String value, long expiresInSeconds) { + RedisUtil.set(key, value, expiresInSeconds); + } + + @Override + public boolean exists(String key) { + return RedisUtil.hasKey(key); + } + + @Override + public void delete(String key) { + RedisUtil.del(key); + } + + @Override + public String get(String key) { + return RedisUtil.get(key); + } + + @Override + public String type() { + return "redis"; + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/CaptchaConfig.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/CaptchaConfig.java new file mode 100644 index 00000000..c410b7cb --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/CaptchaConfig.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.auth.config; + +import cn.hutool.core.io.FileUtil; +import com.anji.captcha.model.common.CaptchaTypeEnum; +import com.anji.captcha.model.common.Const; +import com.anji.captcha.service.CaptchaService; +import com.anji.captcha.service.impl.CaptchaServiceFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +/** + * @author FrozenWatermelon + * @date 2020/8/12 + */ +@Configuration +public class CaptchaConfig { + + @Bean + public CaptchaService captchaService() { + Properties config = new Properties(); + config.put(Const.CAPTCHA_CACHETYPE, "redis"); + config.put(Const.CAPTCHA_WATER_MARK, ""); + // 滑动验证 + config.put(Const.CAPTCHA_TYPE, CaptchaTypeEnum.BLOCKPUZZLE.getCodeValue()); + config.put(Const.ORIGINAL_PATH_JIGSAW, FileUtil.getAbsolutePath("classpath:captcha")); + return CaptchaServiceFactory.getInstance(config); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/SwaggerConfiguration.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/SwaggerConfiguration.java new file mode 100644 index 00000000..32f23c70 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.auth.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.auth.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/constant/AuthAccountStatusEnum.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/constant/AuthAccountStatusEnum.java new file mode 100644 index 00000000..281d91cc --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/constant/AuthAccountStatusEnum.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.auth.constant; + +/** + * 统一账户信息状态 + * @author FrozenWatermelon + * @date 2020/09/22 + */ +public enum AuthAccountStatusEnum { + + /** + * 启用 + */ + ENABLE(1), + + /** + * 禁用 + */ + DISABLE(0), + + /** + * 删除 + */ + DELETE(-1) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + AuthAccountStatusEnum(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/CaptchaController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/CaptchaController.java new file mode 100644 index 00000000..e2b219d1 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/CaptchaController.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.auth.controller; + +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author FrozenWatermelon + * @date 2020/7/30 + */ +@RestController +@RequestMapping("/ua/captcha") +@Api(tags = "验证码") +public class CaptchaController { + + private final CaptchaService captchaService; + + public CaptchaController(CaptchaService captchaService) { + this.captchaService = captchaService; + } + + @PostMapping({ "/get" }) + public ServerResponseEntity get(@RequestBody CaptchaVO captchaVO) { + return ServerResponseEntity.success(captchaService.get(captchaVO)); + } + + @PostMapping({ "/check" }) + public ServerResponseEntity check(@RequestBody CaptchaVO captchaVO) { + return ServerResponseEntity.success(captchaService.check(captchaVO)); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/LoginController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/LoginController.java new file mode 100644 index 00000000..3a8140ea --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/LoginController.java @@ -0,0 +1,88 @@ +package com.mall4j.cloud.auth.controller; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.rbac.dto.ClearUserPermissionsCacheDTO; +import com.mall4j.cloud.api.rbac.feign.PermissionFeignClient; +import com.mall4j.cloud.auth.dto.AuthenticationDTO; +import com.mall4j.cloud.auth.manager.TokenStore; +import com.mall4j.cloud.auth.service.AuthAccountService; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @author FrozenWatermelon + * @date 2020/6/30 + */ +@RestController +@Api(tags = "登录") +public class LoginController { + + @Autowired + private TokenStore tokenStore; + + @Autowired + private AuthAccountService authAccountService; + + @Autowired + private PermissionFeignClient permissionFeignClient; + + @Autowired + private PasswordEncoder passwordEncoder; + + @PostMapping("/ua/login") + @ApiOperation(value = "账号密码", notes = "通过账号登录,还要携带用户的类型,也就是用户所在的系统") + public ServerResponseEntity login( + @Valid @RequestBody AuthenticationDTO authenticationDTO) { + + // 这边获取了用户的用户信息,那么根据sessionid对应一个user的原则,我应该要把这个东西存起来,然后校验,那么存到哪里呢? + // redis,redis有天然的自动过期的机制,有key value的形式 + ServerResponseEntity userInfoInTokenResponse = authAccountService + .getUserInfoInTokenByInputUserNameAndPassword(authenticationDTO.getPrincipal(), + authenticationDTO.getCredentials(), authenticationDTO.getSysType()); + + + if (!userInfoInTokenResponse.isSuccess()) { + return ServerResponseEntity.transform(userInfoInTokenResponse); + } + + UserInfoInTokenBO data = userInfoInTokenResponse.getData(); + + ClearUserPermissionsCacheDTO clearUserPermissionsCacheDTO = new ClearUserPermissionsCacheDTO(); + clearUserPermissionsCacheDTO.setSysType(data.getSysType()); + clearUserPermissionsCacheDTO.setUserId(data.getUserId()); + // 将以前的权限清理了,以免权限有缓存 + ServerResponseEntity clearResponseEntity = permissionFeignClient.clearUserPermissionsCache(clearUserPermissionsCacheDTO); + + if (!clearResponseEntity.isSuccess()) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + + // 保存token,返回token数据给前端,这里是最重要的 + return ServerResponseEntity.success(tokenStore.storeAndGetVo(data)); + } + + @PostMapping("/login_out") + @ApiOperation(value = "退出登陆", notes = "点击退出登陆,清除token,清除菜单缓存") + public ServerResponseEntity loginOut() { + UserInfoInTokenBO userInfoInToken = AuthUserContext.get(); + ClearUserPermissionsCacheDTO clearUserPermissionsCacheDTO = new ClearUserPermissionsCacheDTO(); + clearUserPermissionsCacheDTO.setSysType(userInfoInToken.getSysType()); + clearUserPermissionsCacheDTO.setUserId(userInfoInToken.getUserId()); + // 将以前的权限清理了,以免权限有缓存 + permissionFeignClient.clearUserPermissionsCache(clearUserPermissionsCacheDTO); + // 删除该用户在该系统的token + tokenStore.deleteAllToken(userInfoInToken.getSysType().toString(), userInfoInToken.getUid()); + return ServerResponseEntity.success(); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/PasswordController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/PasswordController.java new file mode 100644 index 00000000..fbc45043 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/PasswordController.java @@ -0,0 +1,52 @@ +package com.mall4j.cloud.auth.controller; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.auth.dto.UpdatePasswordDTO; +import com.mall4j.cloud.auth.manager.TokenStore; +import com.mall4j.cloud.auth.model.AuthAccount; +import com.mall4j.cloud.auth.service.AuthAccountService; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @author FrozenWatermelon + * @date 2021/01/29 + */ +@RestController +@Api(tags = "密码") +public class PasswordController { + + @Autowired + private TokenStore tokenStore; + + @Autowired + private AuthAccountService authAccountService; + + @Autowired + private PasswordEncoder passwordEncoder; + + @PutMapping("/update_password") + @ApiOperation(value = "更新密码", notes = "更新当前用户的密码, 更新密码之后要退出登录,清理token") + public ServerResponseEntity updatePassword(@Valid @RequestBody UpdatePasswordDTO updatePasswordDTO) { + UserInfoInTokenBO userInfoInToken = AuthUserContext.get(); + AuthAccount authAccount = authAccountService.getByUserIdAndType(userInfoInToken.getUserId(), userInfoInToken.getSysType()); + if (!passwordEncoder.matches(updatePasswordDTO.getOldPassword(), authAccount.getPassword())) { + return ServerResponseEntity.showFailMsg("旧密码不正确"); + } + authAccountService.updatePassword(userInfoInToken.getUserId(), userInfoInToken.getSysType(), updatePasswordDTO.getNewPassword()); + tokenStore.deleteAllToken(userInfoInToken.getSysType().toString(), userInfoInToken.getUid()); + return ServerResponseEntity.success(); + } + + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/TokenController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/TokenController.java new file mode 100644 index 00000000..990175da --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/controller/TokenController.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.auth.controller; + +import com.mall4j.cloud.common.security.bo.TokenInfoBO; +import com.mall4j.cloud.auth.dto.RefreshTokenDTO; +import com.mall4j.cloud.auth.manager.TokenStore; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @author FrozenWatermelon + * @date 2020/6/30 + */ +@RestController +@Api(tags = "token") +public class TokenController { + + @Autowired + private TokenStore tokenStore; + + @Autowired + private MapperFacade mapperFacade; + + @PostMapping("/ua/token/refresh") + public ServerResponseEntity refreshToken(@Valid @RequestBody RefreshTokenDTO refreshTokenDTO) { + ServerResponseEntity tokenInfoServerResponseEntity = tokenStore + .refreshToken(refreshTokenDTO.getRefreshToken()); + if (!tokenInfoServerResponseEntity.isSuccess()) { + return ServerResponseEntity.transform(tokenInfoServerResponseEntity); + } + return ServerResponseEntity + .success(mapperFacade.map(tokenInfoServerResponseEntity.getData(), TokenInfoVO.class)); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/AuthenticationDTO.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/AuthenticationDTO.java new file mode 100644 index 00000000..019957e8 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/AuthenticationDTO.java @@ -0,0 +1,69 @@ +package com.mall4j.cloud.auth.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 用于登陆传递账号密码 + * + * @author FrozenWatermelon + * @date 2020/7/1 + */ +public class AuthenticationDTO { + + /** + * 用户名 + */ + @NotBlank(message = "principal不能为空") + @ApiModelProperty(value = "用户名", required = true) + protected String principal; + + /** + * 密码 + */ + @NotBlank(message = "credentials不能为空") + @ApiModelProperty(value = "一般用作密码", required = true) + protected String credentials; + + /** + * sysType 参考SysTypeEnum + */ + @NotNull(message = "sysType不能为空") + @ApiModelProperty(value = "系统类型 0.普通用户系统 1.商家端", required = true) + protected Integer sysType; + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public String getCredentials() { + return credentials; + } + + public void setCredentials(String credentials) { + this.credentials = credentials; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + @Override + public String toString() { + return "AuthenticationDTO{" + + "principal='" + principal + '\'' + + ", credentials='" + credentials + '\'' + + ", sysType=" + sysType + + '}'; + } +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/CaptchaAuthenticationDTO.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/CaptchaAuthenticationDTO.java new file mode 100644 index 00000000..7e1deb0a --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/CaptchaAuthenticationDTO.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.auth.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 验证码登陆 + * + * @author FrozenWatermelon + * @date 2020/7/1 + */ +public class CaptchaAuthenticationDTO extends AuthenticationDTO { + + @ApiModelProperty(value = "验证码", required = true) + private String captchaVerification; + + public String getCaptchaVerification() { + return captchaVerification; + } + + public void setCaptchaVerification(String captchaVerification) { + this.captchaVerification = captchaVerification; + } + + + @Override + public String toString() { + return "CaptchaAuthenticationDTO{" + "captchaVerification='" + captchaVerification + '\'' + "} " + + super.toString(); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/RefreshTokenDTO.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/RefreshTokenDTO.java new file mode 100644 index 00000000..e892e16a --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/RefreshTokenDTO.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.auth.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; + +/** + * 刷新token + * + * @author FrozenWatermelon + * @date 2020/7/1 + */ +public class RefreshTokenDTO { + + /** + * refreshToken + */ + @NotBlank(message = "refreshToken不能为空") + @ApiModelProperty(value = "refreshToken", required = true) + private String refreshToken; + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + @Override + public String toString() { + return "RefreshTokenDTO{" + "refreshToken='" + refreshToken + '\'' + '}'; + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/UpdatePasswordDTO.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/UpdatePasswordDTO.java new file mode 100644 index 00000000..10e6c75b --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/dto/UpdatePasswordDTO.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.auth.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 更新密码 + * + * @author FrozenWatermelon + * @date 2020/09/21 + */ +public class UpdatePasswordDTO { + + @NotBlank(message = "oldPassword NotBlank") + @ApiModelProperty(value = "旧密码", required = true) + private String oldPassword; + + @NotNull(message = "newPassword NotNull") + @ApiModelProperty(value = "新密码", required = true) + private String newPassword; + + public String getOldPassword() { + return oldPassword; + } + + public void setOldPassword(String oldPassword) { + this.oldPassword = oldPassword; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } + + @Override + public String toString() { + return "UpdatePasswordDTO{" + + "oldPassword='" + oldPassword + '\'' + + ", newPassword='" + newPassword + '\'' + + '}'; + } +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/AccountFeignController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/AccountFeignController.java new file mode 100644 index 00000000..a8d86ae8 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/AccountFeignController.java @@ -0,0 +1,158 @@ +package com.mall4j.cloud.auth.feign; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.auth.manager.TokenStore; +import com.mall4j.cloud.auth.mapper.AuthAccountMapper; +import com.mall4j.cloud.auth.model.AuthAccount; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.security.bo.AuthAccountInVerifyBO; +import com.mall4j.cloud.common.security.constant.InputUserNameEnum; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.common.util.PrincipalUtil; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/9/22 + */ +@RestController +public class AccountFeignController implements AccountFeignClient { + + @Autowired + private AuthAccountMapper authAccountMapper; + + @Autowired + private MapperFacade mapperFacade; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private TokenStore tokenStore; + + @Autowired + private SegmentFeignClient segmentFeignClient; + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity save(AuthAccountDTO authAccountDTO) { + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId("mall4cloud-auth-account"); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + + ServerResponseEntity verify = verify(authAccountDTO); + if (!verify.isSuccess()) { + return ServerResponseEntity.transform(verify); + } + AuthAccount data = verify.getData(); + data.setUid(segmentIdResponse.getData()); + authAccountMapper.save(data); + + return ServerResponseEntity.success(data.getUid()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity update(AuthAccountDTO authAccountDTO) { + ServerResponseEntity verify = verify(authAccountDTO); + if (!verify.isSuccess()) { + return ServerResponseEntity.transform(verify); + } + authAccountMapper.updateAccountInfo(verify.getData()); + return ServerResponseEntity.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity updateAuthAccountStatus(AuthAccountDTO authAccountDTO) { + if (Objects.isNull(authAccountDTO.getStatus())) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + AuthAccount authAccount = mapperFacade.map(authAccountDTO, AuthAccount.class); + authAccountMapper.updateAccountInfo(authAccount); + return ServerResponseEntity.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity deleteByUserIdAndSysType(Long userId) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + authAccountMapper.deleteByUserIdAndSysType(userId, userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity getByUserIdAndSysType(Long userId,Integer sysType) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + AuthAccount authAccount = authAccountMapper.getByUserIdAndType(userId, userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(mapperFacade.map(authAccount, AuthAccountVO.class)); + } + + @Override + public ServerResponseEntity storeTokenAndGetVo(UserInfoInTokenBO userInfoInTokenBO) { + return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO)); + } + + @Override + public ServerResponseEntity getByUsernameAndSysType(String username, SysTypeEnum sysType) { + return ServerResponseEntity.success(authAccountMapper.getByUsernameAndSysType(username, sysType.value())); + } + + private ServerResponseEntity verify(AuthAccountDTO authAccountDTO) { + + // 用户名 + if (!PrincipalUtil.isUserName(authAccountDTO.getUsername())) { + return ServerResponseEntity.showFailMsg("用户名格式不正确"); + } + + AuthAccountInVerifyBO userNameBo = authAccountMapper.getAuthAccountInVerifyByInputUserName(InputUserNameEnum.USERNAME.value(), authAccountDTO.getUsername(), authAccountDTO.getSysType()); + if (userNameBo != null && !Objects.equals(userNameBo.getUserId(), authAccountDTO.getUserId())) { + return ServerResponseEntity.showFailMsg("用户名已存在,请更换用户名再次尝试"); + } + + AuthAccount authAccount = mapperFacade.map(authAccountDTO, AuthAccount.class); + + if (StrUtil.isNotBlank(authAccount.getPassword())) { + authAccount.setPassword(passwordEncoder.encode(authAccount.getPassword())); + } + + return ServerResponseEntity.success(authAccount); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity updateUserInfoByUserIdAndSysType(UserInfoInTokenBO userInfoInTokenBO, Long userId, Integer sysType) { + AuthAccount byUserIdAndType = authAccountMapper.getByUserIdAndType(userId, sysType); + userInfoInTokenBO.setUid(byUserIdAndType.getUid()); + tokenStore.updateUserInfoByUidAndAppId(byUserIdAndType.getUid(), sysType.toString(), userInfoInTokenBO); + AuthAccount authAccount = mapperFacade.map(userInfoInTokenBO, AuthAccount.class); + int res = authAccountMapper.updateUserInfoByUserId(authAccount, userId, sysType); + if (res != 1) { + throw new mall4cloudException("用户信息错误,更新失败"); + } + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity getMerchantInfoByTenantId(Long tenantId) { + AuthAccountVO authAccountVO = authAccountMapper.getMerchantInfoByTenantId(tenantId); + return ServerResponseEntity.success(authAccountVO); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/TokenFeignController.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/TokenFeignController.java new file mode 100644 index 00000000..15c521d3 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/feign/TokenFeignController.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.auth.feign; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.feign.TokenFeignClient; +import com.mall4j.cloud.auth.manager.TokenStore; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author FrozenWatermelon + * @date 2020/7/15 + */ +@RestController +public class TokenFeignController implements TokenFeignClient { + + private static final Logger logger = LoggerFactory.getLogger(TokenFeignController.class); + + @Autowired + private TokenStore tokenStore; + + @Override + public ServerResponseEntity checkToken(String accessToken) { + ServerResponseEntity userInfoByAccessTokenResponse = tokenStore + .getUserInfoByAccessToken(accessToken, true); + if (!userInfoByAccessTokenResponse.isSuccess()) { + return ServerResponseEntity.transform(userInfoByAccessTokenResponse); + } + return ServerResponseEntity.success(userInfoByAccessTokenResponse.getData()); + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/manager/TokenStore.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/manager/TokenStore.java new file mode 100644 index 00000000..f6efc50f --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/manager/TokenStore.java @@ -0,0 +1,324 @@ +package com.mall4j.cloud.auth.manager; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.symmetric.AES; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.security.bo.TokenInfoBO; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.common.util.PrincipalUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * token管理 1. 登陆返回token 2. 刷新token 3. 清除用户过去token 4. 校验token + * + * @author FrozenWatermelon + * @date 2020/7/2 + */ +@Component +@RefreshScope +public class TokenStore { + + private static final Logger logger = LoggerFactory.getLogger(TokenStore.class); + + /** + * 用于aes签名的key,16位 + */ + @Value("${auth.token.signKey}") + public String tokenSignKey; + + private final RedisTemplate redisTemplate; + + private final RedisSerializer redisSerializer; + + private final StringRedisTemplate stringRedisTemplate; + + public TokenStore(RedisTemplate redisTemplate, RedisSerializer redisSerializer, + StringRedisTemplate stringRedisTemplate) { + this.redisTemplate = redisTemplate; + this.redisSerializer = redisSerializer; + this.stringRedisTemplate = stringRedisTemplate; + } + + /** + * 将用户的部分信息存储在token中,并返回token信息 + * @param userInfoInToken 用户在token中的信息 + * @return token信息 + */ + public TokenInfoBO storeAccessToken(UserInfoInTokenBO userInfoInToken) { + TokenInfoBO tokenInfoBO = new TokenInfoBO(); + String accessToken = IdUtil.simpleUUID(); + String refreshToken = IdUtil.simpleUUID(); + + tokenInfoBO.setUserInfoInToken(userInfoInToken); + tokenInfoBO.setExpiresIn(getExpiresIn(userInfoInToken.getSysType())); + + String uidToAccessKeyStr = getUidToAccessKey(getApprovalKey(userInfoInToken)); + String accessKeyStr = getAccessKey(accessToken); + String refreshToAccessKeyStr = getRefreshToAccessKey(refreshToken); + + // 一个用户会登陆很多次,每次登陆的token都会存在 uid_to_access里面 + // 但是每次保存都会更新这个key的时间,而key里面的token有可能会过期,过期就要移除掉 + List existsAccessTokens = new ArrayList<>(); + // 新的token数据 + existsAccessTokens.add(accessToken + StrUtil.COLON + refreshToken); + + Long size = redisTemplate.opsForSet().size(uidToAccessKeyStr); + if (size != null && size != 0) { + List tokenInfoBoList = stringRedisTemplate.opsForSet().pop(uidToAccessKeyStr, size); + if (tokenInfoBoList != null) { + for (String accessTokenWithRefreshToken : tokenInfoBoList) { + String[] accessTokenWithRefreshTokenArr = accessTokenWithRefreshToken.split(StrUtil.COLON); + String accessTokenData = accessTokenWithRefreshTokenArr[0]; + if (BooleanUtil.isTrue(stringRedisTemplate.hasKey(getAccessKey(accessTokenData)))) { + existsAccessTokens.add(accessTokenWithRefreshToken); + } + } + } + } + + redisTemplate.executePipelined((RedisCallback) connection -> { + + long expiresIn = tokenInfoBO.getExpiresIn(); + + byte[] uidKey = uidToAccessKeyStr.getBytes(StandardCharsets.UTF_8); + byte[] refreshKey = refreshToAccessKeyStr.getBytes(StandardCharsets.UTF_8); + byte[] accessKey = accessKeyStr.getBytes(StandardCharsets.UTF_8); + + for (String existsAccessToken : existsAccessTokens) { + connection.sAdd(uidKey, existsAccessToken.getBytes(StandardCharsets.UTF_8)); + } + + // 通过uid + sysType 保存access_token,当需要禁用用户的时候,可以根据uid + sysType 禁用用户 + connection.expire(uidKey, expiresIn); + + // 通过refresh_token获取用户的access_token从而刷新token + connection.setEx(refreshKey, expiresIn, accessToken.getBytes(StandardCharsets.UTF_8)); + + // 通过access_token保存用户的租户id,用户id,uid + connection.setEx(accessKey, expiresIn, Objects.requireNonNull(redisSerializer.serialize(userInfoInToken))); + + return null; + }); + + // 返回给前端是加密的token + tokenInfoBO.setAccessToken(encryptToken(accessToken,userInfoInToken.getSysType())); + tokenInfoBO.setRefreshToken(encryptToken(refreshToken,userInfoInToken.getSysType())); + + return tokenInfoBO; + } + + private int getExpiresIn(int sysType) { + // 3600秒 + int expiresIn = 3600; + + // 普通用户token过期时间 1小时 + if (Objects.equals(sysType, SysTypeEnum.ORDINARY.value())) { + expiresIn = expiresIn * 24 * 30; + } + // 系统管理员的token过期时间 2小时 + if (Objects.equals(sysType, SysTypeEnum.MULTISHOP.value()) || Objects.equals(sysType, SysTypeEnum.PLATFORM.value())) { + expiresIn = expiresIn * 24 * 30; + } + return expiresIn; + } + + /** + * 根据accessToken 获取用户信息 + * @param accessToken accessToken + * @param needDecrypt 是否需要解密 + * @return 用户信息 + */ + public ServerResponseEntity getUserInfoByAccessToken(String accessToken, boolean needDecrypt) { + if (StrUtil.isBlank(accessToken)) { + return ServerResponseEntity.showFailMsg("accessToken is blank"); + } + String realAccessToken; + if (needDecrypt) { + ServerResponseEntity decryptTokenEntity = decryptToken(accessToken); + if (!decryptTokenEntity.isSuccess()) { + return ServerResponseEntity.transform(decryptTokenEntity); + } + realAccessToken = decryptTokenEntity.getData(); + } + else { + realAccessToken = accessToken; + } + UserInfoInTokenBO userInfoInTokenBO = (UserInfoInTokenBO) redisTemplate.opsForValue() + .get(getAccessKey(realAccessToken)); + + if (userInfoInTokenBO == null) { + return ServerResponseEntity.showFailMsg("accessToken 已过期"); + } + return ServerResponseEntity.success(userInfoInTokenBO); + } + + /** + * 刷新token,并返回新的token + * @param refreshToken + * @return + */ + public ServerResponseEntity refreshToken(String refreshToken) { + if (StrUtil.isBlank(refreshToken)) { + return ServerResponseEntity.showFailMsg("refreshToken is blank"); + } + ServerResponseEntity decryptTokenEntity = decryptToken(refreshToken); + if (!decryptTokenEntity.isSuccess()) { + return ServerResponseEntity.transform(decryptTokenEntity); + } + String realRefreshToken = decryptTokenEntity.getData(); + String accessToken = stringRedisTemplate.opsForValue().get(getRefreshToAccessKey(realRefreshToken)); + + if (StrUtil.isBlank(accessToken)) { + return ServerResponseEntity.showFailMsg("refreshToken 已过期"); + } + ServerResponseEntity userInfoByAccessTokenEntity = getUserInfoByAccessToken(accessToken, + false); + + if (!userInfoByAccessTokenEntity.isSuccess()) { + return ServerResponseEntity.showFailMsg("refreshToken 已过期"); + } + + UserInfoInTokenBO userInfoInTokenBO = userInfoByAccessTokenEntity.getData(); + // 删除旧的refresh_token + stringRedisTemplate.delete(getRefreshToAccessKey(realRefreshToken)); + // 删除旧的access_token + stringRedisTemplate.delete(getAccessKey(accessToken)); + // 保存一份新的token + TokenInfoBO tokenInfoBO = storeAccessToken(userInfoInTokenBO); + + return ServerResponseEntity.success(tokenInfoBO); + } + + /** + * 删除全部的token + */ + public void deleteAllToken(String appId, Long uid) { + String uidKey = getUidToAccessKey(getApprovalKey(appId, uid)); + Long size = redisTemplate.opsForSet().size(uidKey); + if (size == null || size == 0) { + return; + } + List tokenInfoBoList = stringRedisTemplate.opsForSet().pop(uidKey, size); + + if (CollUtil.isEmpty(tokenInfoBoList)) { + return; + } + + for (String accessTokenWithRefreshToken : tokenInfoBoList) { + String[] accessTokenWithRefreshTokenArr = accessTokenWithRefreshToken.split(StrUtil.COLON); + String accessToken = accessTokenWithRefreshTokenArr[0]; + String refreshToken = accessTokenWithRefreshTokenArr[1]; + redisTemplate.delete(getRefreshToAccessKey(refreshToken)); + redisTemplate.delete(getAccessKey(accessToken)); + } + redisTemplate.delete(uidKey); + + } + + private static String getApprovalKey(UserInfoInTokenBO userInfoInToken) { + return getApprovalKey(userInfoInToken.getSysType().toString(), userInfoInToken.getUid()); + } + + private static String getApprovalKey(String appId, Long uid) { + return uid == null? appId : appId + StrUtil.COLON + uid; + } + + private String encryptToken(String accessToken,Integer sysType) { + AES aes = new AES(tokenSignKey.getBytes(StandardCharsets.UTF_8)); + return aes.encryptBase64(accessToken + System.currentTimeMillis() + sysType); + } + + private ServerResponseEntity decryptToken(String data) { + AES aes = new AES(tokenSignKey.getBytes(StandardCharsets.UTF_8)); + String decryptStr; + String decryptToken; + try { + decryptStr = aes.decryptStr(data); + decryptToken = decryptStr.substring(0,32); + // 创建token的时间,token使用时效性,防止攻击者通过一堆的尝试找到aes的密码,虽然aes是目前几乎最好的加密算法 + long createTokenTime = Long.parseLong(decryptStr.substring(32,45)); + // 系统类型 + int sysType = Integer.parseInt(decryptStr.substring(45)); + // token的过期时间 + int expiresIn = getExpiresIn(sysType); + long second = 1000L; + if (System.currentTimeMillis() - createTokenTime > expiresIn * second) { + return ServerResponseEntity.showFailMsg("token 格式有误"); + } + } + catch (Exception e) { + logger.error(e.getMessage()); + return ServerResponseEntity.showFailMsg("token 格式有误"); + } + + // 防止解密后的token是脚本,从而对redis进行攻击,uuid只能是数字和小写字母 + if (!PrincipalUtil.isSimpleChar(decryptToken)) { + return ServerResponseEntity.showFailMsg("token 格式有误"); + } + return ServerResponseEntity.success(decryptToken); + } + + public String getAccessKey(String accessToken) { + return CacheNames.ACCESS + accessToken; + } + + public String getUidToAccessKey(String approvalKey) { + return CacheNames.UID_TO_ACCESS + approvalKey; + } + + public String getRefreshToAccessKey(String refreshToken) { + return CacheNames.REFRESH_TO_ACCESS + refreshToken; + } + + public TokenInfoVO storeAndGetVo(UserInfoInTokenBO userInfoInToken) { + TokenInfoBO tokenInfoBO = storeAccessToken(userInfoInToken); + + TokenInfoVO tokenInfoVO = new TokenInfoVO(); + tokenInfoVO.setAccessToken(tokenInfoBO.getAccessToken()); + tokenInfoVO.setRefreshToken(tokenInfoBO.getRefreshToken()); + tokenInfoVO.setExpiresIn(tokenInfoBO.getExpiresIn()); + return tokenInfoVO; + } + + public void updateUserInfoByUidAndAppId(Long uid, String appId, UserInfoInTokenBO userInfoInTokenBO) { + if (userInfoInTokenBO == null) { + return; + } + String uidKey = getUidToAccessKey(getApprovalKey(appId, uid)); + Set tokenInfoBoList = stringRedisTemplate.opsForSet().members(uidKey); + if (tokenInfoBoList == null || tokenInfoBoList.size() == 0) { + throw new mall4cloudException(ResponseEnum.UNAUTHORIZED); + } + for (String accessTokenWithRefreshToken : tokenInfoBoList) { + String[] accessTokenWithRefreshTokenArr = accessTokenWithRefreshToken.split(StrUtil.COLON); + String accessKey = this.getAccessKey(accessTokenWithRefreshTokenArr[0]); + UserInfoInTokenBO oldUserInfoInTokenBO = (UserInfoInTokenBO) redisTemplate.opsForValue().get(accessKey); + if (oldUserInfoInTokenBO == null) { + continue; + } + BeanUtils.copyProperties(userInfoInTokenBO, oldUserInfoInTokenBO); + redisTemplate.opsForValue().set(accessKey, Objects.requireNonNull(userInfoInTokenBO),getExpiresIn(userInfoInTokenBO.getSysType()), TimeUnit.SECONDS); + } + } +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/mapper/AuthAccountMapper.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/mapper/AuthAccountMapper.java new file mode 100644 index 00000000..611990fc --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/mapper/AuthAccountMapper.java @@ -0,0 +1,104 @@ +package com.mall4j.cloud.auth.mapper; + +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.auth.model.AuthAccount; +import com.mall4j.cloud.common.security.bo.AuthAccountInVerifyBO; +import org.apache.ibatis.annotations.Param; + +/** + * @author FrozenWatermelon + * @date 2020/7/2 + */ +public interface AuthAccountMapper { + + /** + * 根据输入的用户名及用户名类型获取用户信息 + * + * @param inputUserNameType 输入的用户名类型 1.username 2.mobile 3.email + * @param inputUserName 输入的用户名 + * @param sysType 系统类型 + * @return 用户在token中信息 + 数据库中的密码 + */ + AuthAccountInVerifyBO getAuthAccountInVerifyByInputUserName(@Param("inputUserNameType") Integer inputUserNameType, + @Param("inputUserName") String inputUserName, @Param("sysType") Integer sysType); + + /** + * 根据用户id 和系统类型获取平台唯一用户 + * + * @param userId 用户id + * @param sysType 系统类型 + * @return 平台唯一用户 + */ + AuthAccount getByUserIdAndType(@Param("userId") Long userId, @Param("sysType") Integer sysType); + + /** + * 根据getByUid获取平台唯一用户 + * + * @param uid uid + * @return 平台唯一用户 + */ + AuthAccount getByUid(@Param("uid") Long uid); + + /** + * 更新密码 根据用户id 和系统类型 + * + * @param userId 用户id + * @param sysType 系统类型 + * @param newPassWord 新密码 + */ + void updatePassword(@Param("userId") Long userId, @Param("sysType") Integer sysType, @Param("newPassWord") String newPassWord); + + /** + * 保存 + * + * @param authAccount + */ + void save(@Param("authAccount") AuthAccount authAccount); + + /** + * 更新 + * + * @param authAccount authAccount + */ + void updateAccountInfo(@Param("authAccount") AuthAccount authAccount); + + /** + * 根据用户id和系统类型删除用户 + * + * @param userId 用户id + * @param sysType 系统类型 + */ + void deleteByUserIdAndSysType(@Param("userId") Long userId, @Param("sysType") Integer sysType); + + /** + * 根据用户名和系统类型获取用户信息 + * @param validAccount + * @param systemType + * @return uid + */ + AuthAccount getAccountByInputUserName(@Param("validAccount") String validAccount, @Param("systemType") Integer systemType); + + /** + * 根据用户名和系统类型获取用户信息 + * @param username + * @param sysType + * @return + */ + AuthAccountVO getByUsernameAndSysType(@Param("userName") String username, @Param("sysType") Integer sysType); + + /** + * 根据用户id更新租户id + * @param authAccount + * @param userId + * @param sysType + * @return + */ + int updateUserInfoByUserId(@Param("authAccount") AuthAccount authAccount, @Param("userId") Long userId, @Param("sysType") Integer sysType); + + /** + * 根据租户id获取商家信息 + * @param tenantId + * @return + */ + AuthAccountVO getMerchantInfoByTenantId(@Param("tenantId") Long tenantId); +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/model/AuthAccount.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/model/AuthAccount.java new file mode 100644 index 00000000..7eb813b3 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/model/AuthAccount.java @@ -0,0 +1,147 @@ +package com.mall4j.cloud.auth.model; + +import com.mall4j.cloud.common.database.annotations.DistributedId; +import com.mall4j.cloud.common.model.BaseModel; + +/** + * 统一账户信息 + * + * @author FrozenWatermelon + * @date 2020/07/02 + */ +public class AuthAccount extends BaseModel { + + /** + * 全平台用户唯一id + */ + @DistributedId("mall4cloud-auth-account") + private Long uid; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 创建ip + */ + private String createIp; + + /** + * 状态 1:启用 0:禁用 -1:删除 + */ + private Integer status; + + /** + * 系统类型见SysTypeEnum 0.普通用户系统 1.商家端 + */ + private Integer sysType; + + /** + * 用户id + */ + private Long userId; + + /** + * 所属租户 + */ + private Long tenantId; + + /** + * 是否是管理员 + */ + private Integer isAdmin; + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCreateIp() { + return createIp; + } + + public void setCreateIp(String createIp) { + this.createIp = createIp; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "AuthAccount{" + + "uid=" + uid + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", createIp='" + createIp + '\'' + + ", status=" + status + + ", sysType=" + sysType + + ", userId=" + userId + + ", tenantId=" + tenantId + + ", isAdmin=" + isAdmin + + '}'; + } + +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/AuthAccountService.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/AuthAccountService.java new file mode 100644 index 00000000..a7fe25f0 --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/AuthAccountService.java @@ -0,0 +1,56 @@ +package com.mall4j.cloud.auth.service; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.auth.model.AuthAccount; +import com.mall4j.cloud.common.response.ServerResponseEntity; + +/** + * 统一账户信息 + * + * @author FrozenWatermelon + * @date 2020/7/2 + */ +public interface AuthAccountService { + + /** + * 通过输入的用户名密码,校验并获取部分用户信息 + * @param inputUserName 输入的用户名(用户名) + * @param password 密码 + * @param sysType 系统类型 @see SysTypeEnum + * @return 用户在token中信息 + */ + ServerResponseEntity getUserInfoInTokenByInputUserNameAndPassword(String inputUserName, + String password, Integer sysType); + + /** + * 根据用户id 和系统类型获取平台唯一用户 + * @param userId 用户id + * @param sysType 系统类型 + * @return 平台唯一用户 + */ + AuthAccount getByUserIdAndType(Long userId, Integer sysType); + + /** + * 更新密码 根据用户id 和系统类型 + * @param userId 用户id + * @param sysType 系统类型 + * @param newPassWord 新密码 + */ + void updatePassword(Long userId, Integer sysType, String newPassWord); + + /** + * 根据getByUid获取平台唯一用户 + * + * @param uid uid + * @return 平台唯一用户 + */ + AuthAccount getByUid(Long uid); + + /** + * 根据用户名获取用户信息 + * @param username 用户名 + * @param systemType 系统类型 + * @return uid + */ + AuthAccount getAccountByInputUserName(String username, Integer systemType); +} diff --git a/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/impl/AuthAccountServiceImpl.java b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/impl/AuthAccountServiceImpl.java new file mode 100644 index 00000000..1f9a950a --- /dev/null +++ b/mall4cloud-auth/src/main/java/com/mall4j/cloud/auth/service/impl/AuthAccountServiceImpl.java @@ -0,0 +1,124 @@ +package com.mall4j.cloud.auth.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.auth.constant.AuthAccountStatusEnum; +import com.mall4j.cloud.auth.model.AuthAccount; +import com.mall4j.cloud.common.security.bo.AuthAccountInVerifyBO; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.security.constant.InputUserNameEnum; +import com.mall4j.cloud.auth.mapper.AuthAccountMapper; +import com.mall4j.cloud.auth.service.AuthAccountService; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.PrincipalUtil; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/7/2 + */ +@Service +public class AuthAccountServiceImpl implements AuthAccountService { + + @Resource + private AuthAccountMapper authAccountMapper; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private MapperFacade mapperFacade; + + public static final String USER_NOT_FOUND_SECRET = "USER_NOT_FOUND_SECRET"; + + private static String userNotFoundEncodedPassword; + + @Override + public ServerResponseEntity getUserInfoInTokenByInputUserNameAndPassword(String inputUserName, + String password, Integer sysType) { + + if (StrUtil.isBlank(inputUserName)) { + return ServerResponseEntity.showFailMsg("用户名不能为空"); + } + if (StrUtil.isBlank(password)) { + return ServerResponseEntity.showFailMsg("密码不能为空"); + } + + InputUserNameEnum inputUserNameEnum = null; + + // 用户名 + if (PrincipalUtil.isUserName(inputUserName)) { + inputUserNameEnum = InputUserNameEnum.USERNAME; + } + + if (inputUserNameEnum == null) { + return ServerResponseEntity.showFailMsg("请输入正确的用户名"); + } + + AuthAccountInVerifyBO authAccountInVerifyBO = authAccountMapper + .getAuthAccountInVerifyByInputUserName(inputUserNameEnum.value(), inputUserName, sysType); + + if (authAccountInVerifyBO == null) { + prepareTimingAttackProtection(); + // 再次进行运算,防止计时攻击 + // 计时攻击(Timing + // attack),通过设备运算的用时来推断出所使用的运算操作,或者通过对比运算的时间推定数据位于哪个存储设备,或者利用通信的时间差进行数据窃取。 + mitigateAgainstTimingAttack(password); + return ServerResponseEntity.showFailMsg("用户名或密码不正确"); + } + + if (Objects.equals(authAccountInVerifyBO.getStatus(), AuthAccountStatusEnum.DISABLE.value())) { + return ServerResponseEntity.showFailMsg("用户已禁用,请联系客服"); + } + + if (!passwordEncoder.matches(password, authAccountInVerifyBO.getPassword())) { + return ServerResponseEntity.showFailMsg("用户名或密码不正确"); + } + + return ServerResponseEntity.success(mapperFacade.map(authAccountInVerifyBO, UserInfoInTokenBO.class)); + } + + @Override + public AuthAccount getByUserIdAndType(Long userId, Integer sysType) { + return authAccountMapper.getByUserIdAndType(userId, sysType); + } + + @Override + public void updatePassword(Long userId, Integer sysType, String newPassWord) { + authAccountMapper.updatePassword(userId, sysType, passwordEncoder.encode(newPassWord)); + } + + @Override + public AuthAccount getByUid(Long uid) { + return authAccountMapper.getByUid(uid); + } + + @Override + public AuthAccount getAccountByInputUserName(String mobile, Integer systemType) { + return authAccountMapper.getAccountByInputUserName(mobile,systemType); + } + + /** + * 防止计时攻击 + */ + private void prepareTimingAttackProtection() { + if (userNotFoundEncodedPassword == null) { + userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_SECRET); + } + } + + /** + * 防止计时攻击 + */ + private void mitigateAgainstTimingAttack(String presentedPassword) { + if (presentedPassword != null) { + this.passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword); + } + } + +} diff --git a/mall4cloud-auth/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService b/mall4cloud-auth/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService new file mode 100644 index 00000000..20f6c1a3 --- /dev/null +++ b/mall4cloud-auth/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService @@ -0,0 +1,2 @@ +com.anji.captcha.service.impl.CaptchaCacheServiceMemImpl +com.mall4j.cloud.auth.adapter.CaptchaCacheServiceRedisImpl diff --git a/mall4cloud-auth/src/main/resources/bootstrap.yml b/mall4cloud-auth/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..bc35ffa0 --- /dev/null +++ b/mall4cloud-auth/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9101 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/1.png b/mall4cloud-auth/src/main/resources/captcha/original/1.png new file mode 100644 index 00000000..51573a0c Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/1.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/2.png b/mall4cloud-auth/src/main/resources/captcha/original/2.png new file mode 100644 index 00000000..909dc39e Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/2.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/3.png b/mall4cloud-auth/src/main/resources/captcha/original/3.png new file mode 100644 index 00000000..59bc59c0 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/3.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/4.png b/mall4cloud-auth/src/main/resources/captcha/original/4.png new file mode 100644 index 00000000..c856f4d9 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/4.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/5.png b/mall4cloud-auth/src/main/resources/captcha/original/5.png new file mode 100644 index 00000000..4594fcf6 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/5.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/original/6.png b/mall4cloud-auth/src/main/resources/captcha/original/6.png new file mode 100644 index 00000000..1e044929 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/original/6.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/slidingBlock/1.png b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/1.png new file mode 100644 index 00000000..19050266 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/1.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/slidingBlock/2.png b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/2.png new file mode 100644 index 00000000..b1482d48 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/2.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/slidingBlock/3.png b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/3.png new file mode 100644 index 00000000..cdbb0b18 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/3.png differ diff --git a/mall4cloud-auth/src/main/resources/captcha/slidingBlock/4.png b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/4.png new file mode 100644 index 00000000..bc69c962 Binary files /dev/null and b/mall4cloud-auth/src/main/resources/captcha/slidingBlock/4.png differ diff --git a/mall4cloud-auth/src/main/resources/mapper/AuthAccountMapper.xml b/mall4cloud-auth/src/main/resources/mapper/AuthAccountMapper.xml new file mode 100644 index 00000000..e187566e --- /dev/null +++ b/mall4cloud-auth/src/main/resources/mapper/AuthAccountMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + `uid`, create_time, update_time, username, `password`, create_ip, + `status`, sys_type, user_id, tenant_id, is_admin + + + + + + + + update auth_account set password = #{newPassWord} where sys_type = #{sysType} and user_id = #{userId} + + + insert into auth_account (`uid`,`username`,`password`,`create_ip`,`status`,`sys_type`,`user_id`,`tenant_id`,`is_admin`) + values (#{authAccount.uid},#{authAccount.username},#{authAccount.password},#{authAccount.createIp},#{authAccount.status},#{authAccount.sysType},#{authAccount.userId},#{authAccount.tenantId},#{authAccount.isAdmin}); + + + update auth_account + + + username = #{authAccount.username}, + + + password = #{authAccount.password}, + + + status = #{authAccount.status}, + + + where user_id = #{authAccount.userId} and sys_type = #{authAccount.sysType} + + + update auth_account set status = -1 where user_id = #{userId} and sys_type = #{sysType} + + + update auth_account + + + tenant_id = #{authAccount.tenantId}, + + + where user_id = #{userId} and sys_type = #{sysType} and status != -1 limit 1 + + + + + + diff --git a/mall4cloud-biz/pom.xml b/mall4cloud-biz/pom.xml new file mode 100644 index 00000000..f3849635 --- /dev/null +++ b/mall4cloud-biz/pom.xml @@ -0,0 +1,81 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-biz + jar + + mall4cloud 业务代码。如图片上传等 + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-cache + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-biz + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-user + ${project.version} + + + io.minio + minio + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/BizServerApplication.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/BizServerApplication.java new file mode 100644 index 00000000..1007a1c0 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/BizServerApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.biz; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/9/10 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class BizServerApplication { + + public static void main(String[] args) { + SpringApplication.run(BizServerApplication.class, args); + } + +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/MinioTemplate.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/MinioTemplate.java new file mode 100644 index 00000000..50d86963 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/MinioTemplate.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.biz.config; + +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import io.minio.*; +import io.minio.http.Method; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @author FrozenWatermelon + */ +@Component +public class MinioTemplate implements InitializingBean { + + @Autowired + private OssConfig ossConfig; + + private MinioClient minioClient; + + @Override + public void afterPropertiesSet() throws Exception { + this.minioClient = MinioClient.builder().endpoint(ossConfig.getEndpoint()) + .credentials(ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()) + .build(); + } + + + /** + * 删除文件 + * + * @param objectName 文件名称 + * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject + */ + public void removeObject(String objectName) throws Exception { + minioClient.removeObject(RemoveObjectArgs.builder().object(objectName).bucket(ossConfig.getBucket()).build()); + } + + /** + * 获得上传的URL + * @param objectName + */ + public String getPresignedObjectUrl(String objectName){ + try { + String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(ossConfig.getBucket()).object(objectName).expiry(10, TimeUnit.MINUTES).method(Method.PUT).build()); + return presignedObjectUrl; + } catch (Exception e) { + e.printStackTrace(); + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/OssConfig.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/OssConfig.java new file mode 100644 index 00000000..e1266cd8 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/OssConfig.java @@ -0,0 +1,84 @@ +package com.mall4j.cloud.biz.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author FrozenWatermelon + * @date 2020/9/10 + */ +@RefreshScope +@Configuration +public class OssConfig { + + @Value("${biz.oss.endpoint}") + private String endpoint; + @Value("${biz.oss.bucket}") + private String bucket; + @Value("${biz.oss.access-key-id}") + private String accessKeyId; + @Value("${biz.oss.access-key-secret}") + private String accessKeySecret; + @Value("${biz.oss.type}") + private Integer ossType; + + /** + * 最大上传长度单位m,默认20M + */ + @Value("${biz.oss.maxLength:20}") + private Integer maxLength; + + public String getAccessId() { + return accessKeyId; + } + + public String getBucket() { + return bucket; + } + + public String getEndpoint() { + return endpoint; + } + + public Integer getMaxLength() { + return maxLength; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public String getAccessKeyId() { + return accessKeyId; + } + + public void setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public String getAccessKeySecret() { + return accessKeySecret; + } + + public void setAccessKeySecret(String accessKeySecret) { + this.accessKeySecret = accessKeySecret; + } + + public void setMaxLength(Integer maxLength) { + this.maxLength = maxLength; + } + + public Integer getOssType() { + return ossType; + } + + public void setOssType(Integer ossType) { + this.ossType = ossType; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/SwaggerConfiguration.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/SwaggerConfiguration.java new file mode 100644 index 00000000..5c630fc8 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.biz.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.biz.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/constant/OssType.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/constant/OssType.java new file mode 100644 index 00000000..5edf567b --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/constant/OssType.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.biz.constant; + +/** + * 文件上传存储类型 + * @author FrozenWatermelon + * @date 2021/01/20 + */ +public enum OssType { + + /** + * 阿里云oss + */ + ALI(0), + + /** + * minio + */ + MINIO(1), +; + + private final Integer value; + + public Integer value() { + return value; + } + + OssType(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/OssController.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/OssController.java new file mode 100644 index 00000000..c9859396 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/OssController.java @@ -0,0 +1,79 @@ +package com.mall4j.cloud.biz.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import com.mall4j.cloud.biz.config.MinioTemplate; +import com.mall4j.cloud.biz.config.OssConfig; +import com.mall4j.cloud.biz.constant.OssType; +import com.mall4j.cloud.biz.vo.OssVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/9/10 + */ +@RequestMapping(value = "/oss") +@RestController +@Api(tags = "文件管理") +public class OssController { + + private static final Logger log = LoggerFactory.getLogger(OssController.class); + + /** + * 上传的文件夹(根据时间确定) + */ + public static final String NORM_DAY_PATTERN = "yyyy/MM/dd"; + + @Autowired + private OssConfig ossConfig; + @Autowired + private MinioTemplate minioTemplate; + + + @GetMapping(value = "/info") + @ApiOperation(value = "token", notes = "获取文件上传需要的token") + @ApiImplicitParam(name = "fileNum", value = "需要获取token的文件数量", defaultValue = "0") + public ServerResponseEntity info(@RequestParam("fileNum") Integer fileNum) { + OssVO ossVO = new OssVO(); + // minio文件上传 + if (Objects.equals(ossConfig.getOssType(), OssType.MINIO.value())) { + fillMinIoInfo(ossVO, fileNum); + } + return ServerResponseEntity.success(ossVO); + } + + private void fillMinIoInfo(OssVO ossVo, Integer fileNum) { + List ossVOList = new ArrayList<>(); + for (int i = 0; i> page(@Valid PageDTO pageDTO, String fileName, Long fileGroupId) { + if (fileGroupId == 0) { + fileGroupId = null; + } + PageVO attachFilePage = attachFileService.page(pageDTO, fileName, fileGroupId); + return ServerResponseEntity.success(attachFilePage); + } + + @PostMapping + @ApiOperation(value = "保存上传文件记录", notes = "保存上传文件记录") + public ServerResponseEntity save(@RequestBody List attachFileDtos) { + List attachFiles = mapperFacade.mapAsList(attachFileDtos, AttachFile.class); + attachFileService.save(attachFiles); + return ServerResponseEntity.success(); + } + + /** + * 更改文件名或分组 + */ + @PutMapping("/update_file") + @ApiOperation(value = "更新文件记录", notes = "更新文件记录") + public ServerResponseEntity updateFileName(@RequestBody AttachFileDTO attachFileDto) { + if (Objects.isNull(attachFileDto.getFileName())) { + // 图片名称不能为空 + throw new mall4cloudException("图片名称不能为空"); + } + AttachFile attachFile = mapperFacade.map(attachFileDto, AttachFile.class); + return ServerResponseEntity.success(attachFileService.updateFileName(attachFile)); + } + + @DeleteMapping + @ApiOperation(value = "删除上传文件记录", notes = "根据上传文件记录表id删除上传文件记录") + public ServerResponseEntity delete(@RequestParam Long fileId) { + attachFileService.deleteById(fileId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/multishop/AttachFileGroupController.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/multishop/AttachFileGroupController.java new file mode 100644 index 00000000..1430a655 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/controller/multishop/AttachFileGroupController.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.biz.controller.multishop; + +import com.mall4j.cloud.biz.dto.AttachFileGroupDTO; +import com.mall4j.cloud.biz.model.AttachFileGroup; +import com.mall4j.cloud.biz.service.AttachFileGroupService; +import com.mall4j.cloud.biz.vo.AttachFileGroupVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.List; + +/** + * + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +@RestController("multishopAttachFileGroupController") +@RequestMapping("/m/attach_file_group") +@Api(tags = "店铺-文件分组") +public class AttachFileGroupController { + @Autowired + private AttachFileGroupService attachFileGroupService; + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/list") + @ApiOperation(value = "获取列表", notes = "分页获取列表") + public ServerResponseEntity> list() { + List attachFileGroupPage = attachFileGroupService.list(); + return ServerResponseEntity.success(attachFileGroupPage); + } + + @GetMapping + @ApiOperation(value = "获取", notes = "根据attachFileGroupId获取") + public ServerResponseEntity getByAttachFileGroupId(@RequestParam Long attachFileGroupId) { + return ServerResponseEntity.success(attachFileGroupService.getByAttachFileGroupId(attachFileGroupId)); + } + + @PostMapping + @ApiOperation(value = "保存", notes = "保存") + public ServerResponseEntity save(@Valid @RequestBody AttachFileGroupDTO attachFileGroupDTO) { + AttachFileGroup attachFileGroup = mapperFacade.map(attachFileGroupDTO, AttachFileGroup.class); + attachFileGroup.setAttachFileGroupId(null); + attachFileGroupService.save(attachFileGroup); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新", notes = "更新") + public ServerResponseEntity update(@Valid @RequestBody AttachFileGroupDTO attachFileGroupDTO) { + AttachFileGroup attachFileGroup = mapperFacade.map(attachFileGroupDTO, AttachFileGroup.class); + attachFileGroupService.update(attachFileGroup); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除", notes = "根据id删除") + public ServerResponseEntity delete(@RequestParam Long attachFileGroupId) { + attachFileGroupService.deleteById(attachFileGroupId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileDTO.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileDTO.java new file mode 100644 index 00000000..7987b051 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileDTO.java @@ -0,0 +1,115 @@ +package com.mall4j.cloud.biz.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 上传文件记录表DTO + * + * @author YXF + * @date 2020-11-21 10:21:40 + */ +public class AttachFileDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long fileId; + + @ApiModelProperty("文件路径") + private String filePath; + + @ApiModelProperty("文件类型") + private String fileType; + + @ApiModelProperty("文件名") + private String fileName; + + @ApiModelProperty("文件大小") + private Integer fileSize; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("文件 1:图片 2:视频 3:文件") + private Integer type; + + @ApiModelProperty("文件分组id") + private Long attachFileGroupId; + + public Long getFileId() { + return fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + @Override + public String toString() { + return "AttachFileDTO{" + + "fileId=" + fileId + + ", filePath='" + filePath + '\'' + + ", fileType='" + fileType + '\'' + + ", fileName='" + fileName + '\'' + + ", fileSize=" + fileSize + + ", shopId=" + shopId + + ", type=" + type + + ", attachFileGroupId=" + attachFileGroupId + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileGroupDTO.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileGroupDTO.java new file mode 100644 index 00000000..377e4926 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/dto/AttachFileGroupDTO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.biz.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * DTO + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +public class AttachFileGroupDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long attachFileGroupId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("分组名称") + private String name; + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "AttachFileGroupDTO{" + + "attachFileGroupId=" + attachFileGroupId + + ",shopId=" + shopId + + ",name=" + name + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileGroupMapper.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileGroupMapper.java new file mode 100644 index 00000000..e6e0e61a --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileGroupMapper.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.biz.mapper; + +import com.mall4j.cloud.biz.model.AttachFileGroup; +import com.mall4j.cloud.biz.vo.AttachFileGroupVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author YXF + * @date 2020-12-04 16:15:02 + */ +public interface AttachFileGroupMapper { + + /** + * 获取列表 + * @param shopId + * @return 列表 + */ + List list(@Param("shopId") Long shopId); + + /** + * 根据id获取 + * + * @param attachFileGroupId id + * @return + */ + AttachFileGroupVO getByAttachFileGroupId(@Param("attachFileGroupId") Long attachFileGroupId); + + /** + * 保存 + * + * @param attachFileGroup + */ + void save(@Param("attachFileGroup") AttachFileGroup attachFileGroup); + + /** + * 更新 + * + * @param attachFileGroup + */ + void update(@Param("attachFileGroup") AttachFileGroup attachFileGroup); + + /** + * 根据id删除 + * + * @param attachFileGroupId + */ + void deleteById(@Param("attachFileGroupId") Long attachFileGroupId); +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileMapper.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileMapper.java new file mode 100644 index 00000000..278af586 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/mapper/AttachFileMapper.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.biz.mapper; + + +import com.mall4j.cloud.biz.model.AttachFile; +import com.mall4j.cloud.biz.vo.AttachFileVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 上传文件记录表 + * + * @author YXF + * @date 2020-11-21 10:21:40 + */ +public interface AttachFileMapper { + + /** + * 获取上传文件记录表列表 + * + * @param fileName 文件名称 + * @param shopId + * @param fileGroupId 文件分组id + * @return 文件记录表列表 + */ + List list(@Param("fileName") String fileName, @Param("shopId") Long shopId, @Param("fileGroupId") Long fileGroupId); + + /** + * 保存上传文件记录表 + * + * @param attachFiles 上传文件记录表 + * @param shopId 店铺id + */ + void save(@Param("attachFiles") List attachFiles, @Param("shopId") Long shopId); + + /** + * 更新上传文件记录表 + * + * @param attachFile + * @param attachFile 上传文件记录表 + */ + void update(@Param("attachFile") AttachFile attachFile); + + /** + * 根据上传文件记录表id删除上传文件记录表 + * + * @param fileId + */ + void deleteById(@Param("fileId") Long fileId); + + /** + * 根据id获取文件信息 + * + * @param fileId + * @return + */ + AttachFile getById(@Param("fileId") Long fileId); + + /** + * 批量更新文件的分组 + * @param attachFileGroupId + */ + void updateBatchByAttachFileGroupId(@Param("attachFileGroupId") Long attachFileGroupId); +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFile.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFile.java new file mode 100644 index 00000000..6175f69d --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFile.java @@ -0,0 +1,135 @@ +package com.mall4j.cloud.biz.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 上传文件记录表 + * + * @author FrozenWatermelon + * @date 2020-11-21 10:21:40 + */ +public class AttachFile extends BaseModel implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * + */ + private Long fileId; + + /** + * 文件路径 + */ + private String filePath; + + /** + * 文件类型 + */ + private String fileType; + + /** + * 文件名 + */ + private String fileName; + + /** + * 文件大小 + */ + private Integer fileSize; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 文件 1:图片 2:视频 3:文件 + */ + private Integer type; + + /** + * 文件分组id(0:默认为所有) + */ + private Long attachFileGroupId; + + public Long getFileId() { + return fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + @Override + public String toString() { + return "AttachFile{" + + "fileId=" + fileId + + ", filePath='" + filePath + '\'' + + ", fileType='" + fileType + '\'' + + ", fileName='" + fileName + '\'' + + ", fileSize=" + fileSize + + ", shopId=" + shopId + + ", type=" + type + + ", attachFileGroupId=" + attachFileGroupId + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFileGroup.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFileGroup.java new file mode 100644 index 00000000..a6d42fe7 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/model/AttachFileGroup.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.biz.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +public class AttachFileGroup extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * + */ + private Long attachFileGroupId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 分组名称 + */ + private String name; + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "AttachFileGroup{" + + "attachFileGroupId=" + attachFileGroupId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",shopId=" + shopId + + ",name=" + name + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileGroupService.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileGroupService.java new file mode 100644 index 00000000..3ff60900 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileGroupService.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.biz.service; + +import com.mall4j.cloud.biz.model.AttachFileGroup; +import com.mall4j.cloud.biz.vo.AttachFileGroupVO; + +import java.util.List; + +/** + * + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +public interface AttachFileGroupService { + + /** + * 获取列表 + * @return 列表数据 + */ + List list(); + + /** + * 根据id获取 + * + * @param attachFileGroupId id + * @return + */ + AttachFileGroupVO getByAttachFileGroupId(Long attachFileGroupId); + + /** + * 保存 + * @param attachFileGroup + */ + void save(AttachFileGroup attachFileGroup); + + /** + * 更新 + * @param attachFileGroup + */ + void update(AttachFileGroup attachFileGroup); + + /** + * 根据id删除 + * @param attachFileGroupId + */ + void deleteById(Long attachFileGroupId); +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileService.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileService.java new file mode 100644 index 00000000..6a2cb891 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/AttachFileService.java @@ -0,0 +1,51 @@ +package com.mall4j.cloud.biz.service; + +import com.mall4j.cloud.biz.model.AttachFile; +import com.mall4j.cloud.biz.vo.AttachFileVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; + +import java.util.List; + +/** + * 上传文件记录表 + * + * @author YXF + * @date 2020-11-21 10:21:40 + */ +public interface AttachFileService { + + /** + * 分页获取上传文件记录表列表 + * @param pageDTO 分页参数 + * @param fileName 文件名 + * @param fileGroupId 文件分组id + * @return 上传文件记录表列表分页数据 + */ + PageVO page(PageDTO pageDTO, String fileName, Long fileGroupId); + + /** + * 保存上传文件记录表 + * @param attachFiles 上传文件记录表 + */ + void save(List attachFiles); + + /** + * 更新上传文件记录表 + * @param attachFile 上传文件记录表 + */ + void update(AttachFile attachFile); + + /** + * 根据上传文件记录表id删除上传文件记录表 + * @param fileId + */ + void deleteById(Long fileId); + + /** + * 更新文件名称 + * @param attachFile + * @return + */ + Boolean updateFileName(AttachFile attachFile); +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileGroupServiceImpl.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileGroupServiceImpl.java new file mode 100644 index 00000000..359704fc --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileGroupServiceImpl.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.biz.service.impl; + +import com.mall4j.cloud.biz.mapper.AttachFileMapper; +import com.mall4j.cloud.biz.model.AttachFileGroup; +import com.mall4j.cloud.biz.mapper.AttachFileGroupMapper; +import com.mall4j.cloud.biz.service.AttachFileGroupService; +import com.mall4j.cloud.biz.vo.AttachFileGroupVO; +import com.mall4j.cloud.common.security.AuthUserContext; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +@Service +public class AttachFileGroupServiceImpl implements AttachFileGroupService { + + @Autowired + private AttachFileMapper attachFileMapper; + @Autowired + private AttachFileGroupMapper attachFileGroupMapper; + + @Override + public List list() { + return attachFileGroupMapper.list(AuthUserContext.get().getTenantId()); + } + + @Override + public AttachFileGroupVO getByAttachFileGroupId(Long attachFileGroupId) { + return attachFileGroupMapper.getByAttachFileGroupId(attachFileGroupId); + } + + @Override + public void save(AttachFileGroup attachFileGroup) { + attachFileGroup.setShopId(AuthUserContext.get().getTenantId()); + attachFileGroupMapper.save(attachFileGroup); + } + + @Override + public void update(AttachFileGroup attachFileGroup) { + attachFileGroupMapper.update(attachFileGroup); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long attachFileGroupId) { + attachFileGroupMapper.deleteById(attachFileGroupId); + attachFileMapper.updateBatchByAttachFileGroupId(attachFileGroupId); + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileServiceImpl.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileServiceImpl.java new file mode 100644 index 00000000..c3e0294c --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/service/impl/AttachFileServiceImpl.java @@ -0,0 +1,85 @@ +package com.mall4j.cloud.biz.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.biz.config.MinioTemplate; +import com.mall4j.cloud.biz.mapper.AttachFileMapper; +import com.mall4j.cloud.biz.model.AttachFile; +import com.mall4j.cloud.biz.service.AttachFileService; +import com.mall4j.cloud.biz.vo.AttachFileVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.security.AuthUserContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.Objects; + +/** + * 上传文件记录表 + * + * @author YXF + * @date 2020-11-21 10:21:40 + */ +@Service +public class AttachFileServiceImpl implements AttachFileService { + + @Autowired + private AttachFileMapper attachFileMapper; + + @Autowired + private Environment environment; + + @Autowired + private MinioTemplate minioTemplate; + + @Override + public PageVO page(PageDTO pageDTO, String fileName, Long fileGroupId) { + return PageUtil.doPage(pageDTO, () -> attachFileMapper.list(fileName, AuthUserContext.get().getTenantId(), fileGroupId)); + } + + @Override + public void save(List attachFiles) { + attachFileMapper.save(attachFiles, AuthUserContext.get().getTenantId()); + } + + @Override + public void update(AttachFile attachFile) { + attachFileMapper.update(attachFile); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long fileId) { + AttachFile attachFile = attachFileMapper.getById(fileId); + deleteFile(attachFile.getFilePath()); + attachFileMapper.deleteById(fileId); + } + + @Override + public Boolean updateFileName(AttachFile attachFile) { + if (Objects.isNull(attachFile.getFileName()) && Objects.isNull(attachFile.getAttachFileGroupId())) { + return Boolean.TRUE; + } + attachFileMapper.update(attachFile); + return Boolean.TRUE; + } + + /** + * 根据文件路径,删除文件 + * @param filePath 文件路径 + */ + public void deleteFile(String filePath){ + // 获取文件的实际路径--数据库中保存的文件路径为: / + 实际的文件路径 + if (StrUtil.isNotBlank(filePath)) { + filePath = filePath.substring(1); + } + try { + minioTemplate.removeObject(filePath); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileGroupVO.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileGroupVO.java new file mode 100644 index 00000000..8ac0b9bb --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileGroupVO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.biz.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * VO + * + * @author YXF + * @date 2020-12-04 16:15:02 + */ +public class AttachFileGroupVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long attachFileGroupId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("分组名称") + private String name; + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "AttachFileGroupVO{" + + "attachFileGroupId=" + attachFileGroupId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",shopId=" + shopId + + ",name=" + name + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileVO.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileVO.java new file mode 100644 index 00000000..df806331 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/AttachFileVO.java @@ -0,0 +1,118 @@ +package com.mall4j.cloud.biz.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 上传文件记录表VO + * + * @author YXF + * @date 2020-11-21 10:21:40 + */ +public class AttachFileVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long fileId; + + @ApiModelProperty("文件路径") + private String filePath; + + @ApiModelProperty("文件类型") + private String fileType; + + @ApiModelProperty("文件名") + private String fileName; + + @ApiModelProperty("文件大小") + private Integer fileSize; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("文件 1:图片 2:视频 3:文件") + private Integer type; + + @ApiModelProperty("分组id") + private Long attachFileGroupId; + + public Long getFileId() { + return fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Long getAttachFileGroupId() { + return attachFileGroupId; + } + + public void setAttachFileGroupId(Long attachFileGroupId) { + this.attachFileGroupId = attachFileGroupId; + } + + @Override + public String toString() { + return "AttachFileVO{" + + "fileId=" + fileId + + ", filePath='" + filePath + '\'' + + ", fileType='" + fileType + '\'' + + ", fileName='" + fileName + '\'' + + ", fileSize=" + fileSize + + ", shopId=" + shopId + + ", type=" + type + + ", attachFileGroupId=" + attachFileGroupId + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/OssVO.java b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/OssVO.java new file mode 100644 index 00000000..1f4ac613 --- /dev/null +++ b/mall4cloud-biz/src/main/java/com/mall4j/cloud/biz/vo/OssVO.java @@ -0,0 +1,117 @@ +package com.mall4j.cloud.biz.vo; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/9/12 + */ +public class OssVO { + + private String accessid; + + private String policy; + + private String signature; + + private String dir; + + private String host; + + private Integer expire; + + private String fileName; + + private String actionUrl; + + /** + * url列表--minio中一条链接对应一个上传的文件 + * @return + */ + private List ossList; + + public String getAccessid() { + return accessid; + } + + public void setAccessid(String accessid) { + this.accessid = accessid; + } + + public String getPolicy() { + return policy; + } + + public void setPolicy(String policy) { + this.policy = policy; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getDir() { + return dir; + } + + public void setDir(String dir) { + this.dir = dir; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getExpire() { + return expire; + } + + public void setExpire(Integer expire) { + this.expire = expire; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getActionUrl() { + return actionUrl; + } + + public void setActionUrl(String actionUrl) { + this.actionUrl = actionUrl; + } + + public List getOssList() { + return ossList; + } + + public void setOssList(List ossList) { + this.ossList = ossList; + } + + @Override + public String toString() { + return "OssVO{" + + "accessid='" + accessid + '\'' + + ", policy='" + policy + '\'' + + ", signature='" + signature + '\'' + + ", dir='" + dir + '\'' + + ", host='" + host + '\'' + + ", expire='" + expire + '\'' + + ", ossList='" + ossList + '\'' + + '}'; + } +} diff --git a/mall4cloud-biz/src/main/resources/bootstrap.yml b/mall4cloud-biz/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..302d1822 --- /dev/null +++ b/mall4cloud-biz/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9000 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-biz/src/main/resources/mapper/AttachFileGroupMapper.xml b/mall4cloud-biz/src/main/resources/mapper/AttachFileGroupMapper.xml new file mode 100644 index 00000000..edc89986 --- /dev/null +++ b/mall4cloud-biz/src/main/resources/mapper/AttachFileGroupMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + `attach_file_group_id`,`create_time`,`update_time`,`shop_id`,`name` + + + + + insert into attach_file_group (`shop_id`,`name`) + values (#{attachFileGroup.shopId},#{attachFileGroup.name}); + + + update attach_file_group + + + `shop_id` = #{attachFileGroup.shopId}, + + + `name` = #{attachFileGroup.name}, + + + where attach_file_group_id = #{attachFileGroup.attachFileGroupId} + + + delete from attach_file_group where attach_file_group_id = #{attachFileGroupId} + + + diff --git a/mall4cloud-biz/src/main/resources/mapper/AttachFileMapper.xml b/mall4cloud-biz/src/main/resources/mapper/AttachFileMapper.xml new file mode 100644 index 00000000..61b19793 --- /dev/null +++ b/mall4cloud-biz/src/main/resources/mapper/AttachFileMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + `file_id`,`file_path`,`file_type`,`file_name`,`file_size`,`shop_id`,`type`,`create_time`,`update_time`,`attach_file_group_id` + + + + insert into attach_file (`file_path`,`file_type`,`file_name`,`file_size`,`shop_id`,`type`,`attach_file_group_id`) + values + + (#{attachFile.filePath},#{attachFile.fileType},#{attachFile.fileName},#{attachFile.fileSize},#{shopId},#{attachFile.type},#{attachFile.attachFileGroupId}) + + + + update attach_file + + + `file_name` = #{attachFile.fileName}, + + + `attach_file_group_id` = #{attachFile.attachFileGroupId}, + + + where file_id = #{attachFile.fileId} + + + delete from attach_file where file_id = #{fileId} + + + + + update attach_file + set attach_file_group_id = 0 + where attach_file_group_id = #{attachFileGroupId} + + diff --git a/mall4cloud-common/mall4cloud-common-cache/README.md b/mall4cloud-common/mall4cloud-common-cache/README.md new file mode 100644 index 00000000..49147257 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/README.md @@ -0,0 +1,9 @@ +### 一些通用的缓存配置 + +缓存key命名规范: + +服务名:业务名:方法名:key, 大小写以下划线分割 如 +mall4cloud_oauth:token:access: +mall4cloud_oauth:token:refresh_to_access: +mall4cloud_oauth:token:uid_to_access: + diff --git a/mall4cloud-common/mall4cloud-common-cache/pom.xml b/mall4cloud-common/mall4cloud-common-cache/pom.xml new file mode 100644 index 00000000..86906f04 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/pom.xml @@ -0,0 +1,36 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-cache + jar + mall4cloud 缓存相关代码 + + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-integration + + + + org.springframework.integration + spring-integration-redis + + + diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/adapter/CacheTtlAdapter.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/adapter/CacheTtlAdapter.java new file mode 100644 index 00000000..1f0bd726 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/adapter/CacheTtlAdapter.java @@ -0,0 +1,21 @@ +package com.mall4j.cloud.common.cache.adapter; + +import com.mall4j.cloud.common.cache.bo.CacheNameWithTtlBO; + +import java.util.List; + +/** + * 实现该接口之后,根据缓存的cacheName和ttl将缓存进行过期 + * + * @author FrozenWatermelon + * @date 2020/7/4 + */ +public interface CacheTtlAdapter { + + /** + * 根据缓存的cacheName和ttl将缓存进行过期 + * @return 需要独立设置过期时间的缓存列表 + */ + List listCacheNameWithTtl(); + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/bo/CacheNameWithTtlBO.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/bo/CacheNameWithTtlBO.java new file mode 100644 index 00000000..86367a74 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/bo/CacheNameWithTtlBO.java @@ -0,0 +1,41 @@ +package com.mall4j.cloud.common.cache.bo; + +/** + * 通过 cacheName 配置 和 时间告诉缓存多久清楚一遍 + * + * @author FrozenWatermelon + * @date 2020/7/4 + */ +public class CacheNameWithTtlBO { + + private String cacheName; + + private Integer ttl; + + public CacheNameWithTtlBO(String cacheName, Integer ttl) { + this.cacheName = cacheName; + this.ttl = ttl; + } + + public String getCacheName() { + return cacheName; + } + + public void setCacheName(String cacheName) { + this.cacheName = cacheName; + } + + public Integer getTtl() { + return ttl; + } + + public void setTtl(Integer ttl) { + this.ttl = ttl; + } + + @Override + public String toString() { + return "CacheNameWithTtlBO{" + "cacheName='" + cacheName + '\'' + ", ttl=" + ttl + '}'; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisCacheConfig.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisCacheConfig.java new file mode 100644 index 00000000..1e0744a8 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisCacheConfig.java @@ -0,0 +1,126 @@ +package com.mall4j.cloud.common.cache.config; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.mall4j.cloud.common.cache.adapter.CacheTtlAdapter; +import com.mall4j.cloud.common.cache.bo.CacheNameWithTtlBO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @author FrozenWatermelon + * @date 2020/7/4 + */ +@EnableCaching +@Configuration +public class RedisCacheConfig { + + @Bean + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheTtlAdapter cacheTtlAdapter) { + + RedisCacheManager redisCacheManager = new RedisCacheManager( + RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), + // 默认策略,未配置的 key 会使用这个 + this.getRedisCacheConfigurationWithTtl(3600), + // 指定 key 策略 + this.getRedisCacheConfigurationMap(cacheTtlAdapter)); + + redisCacheManager.setTransactionAware(true); + return redisCacheManager; + } + + private Map getRedisCacheConfigurationMap(CacheTtlAdapter cacheTtlAdapter) { + if (cacheTtlAdapter == null) { + return Collections.emptyMap(); + } + Map redisCacheConfigurationMap = new HashMap<>(16); + + for (CacheNameWithTtlBO cacheNameWithTtlBO : cacheTtlAdapter.listCacheNameWithTtl()) { + redisCacheConfigurationMap.put(cacheNameWithTtlBO.getCacheName(), + getRedisCacheConfigurationWithTtl(cacheNameWithTtlBO.getTtl())); + } + return redisCacheConfigurationMap; + } + + private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); + redisCacheConfiguration = redisCacheConfiguration + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())) + .entryTtl(Duration.ofSeconds(seconds)); + + return redisCacheConfiguration; + } + + /** + * 自定义redis序列化的机制,重新定义一个ObjectMapper.防止和MVC的冲突 + * https://juejin.im/post/5e869d426fb9a03c6148c97e + */ + @Bean + public RedisSerializer redisSerializer() { + ObjectMapper objectMapper = new ObjectMapper(); + // 反序列化时候遇到不匹配的属性并不抛出异常 + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // 序列化时候遇到空对象不抛出异常 + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // 反序列化的时候如果是无效子类型,不抛出异常 + objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + // 不使用默认的dateTime进行序列化, + objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false); + // 使用JSR310提供的序列化类,里面包含了大量的JDK8时间序列化类 + objectMapper.registerModule(new JavaTimeModule()); + // 启用反序列化所需的类型信息,在属性中添加@class + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); + // 配置null值的序列化器 + GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null); + return new GenericJackson2JsonRedisSerializer(objectMapper); + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory, + RedisSerializer redisSerializer) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + template.setDefaultSerializer(redisSerializer); + template.setValueSerializer(redisSerializer); + template.setHashValueSerializer(redisSerializer); + template.setKeySerializer(StringRedisSerializer.UTF_8); + template.setHashKeySerializer(StringRedisSerializer.UTF_8); + template.afterPropertiesSet(); + return template; + } + + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + return new StringRedisTemplate(redisConnectionFactory); + } + + @Bean + @ConditionalOnMissingBean + public CacheTtlAdapter cacheTtl() { + return Collections::emptyList; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisLockConfiguration.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisLockConfiguration.java new file mode 100644 index 00000000..8d52e9fa --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/config/RedisLockConfiguration.java @@ -0,0 +1,18 @@ +package com.mall4j.cloud.common.cache.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.integration.redis.util.RedisLockRegistry; + +/** + * @author FrozenWatermelon + * @date 2020/12/29 + */ +@Configuration +public class RedisLockConfiguration { + @Bean + public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){ + return new RedisLockRegistry(redisConnectionFactory,"spring-cloud"); + } +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/BizCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/BizCacheNames.java new file mode 100644 index 00000000..e1b04fb5 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/BizCacheNames.java @@ -0,0 +1,14 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2021/01/25 + */ +public interface BizCacheNames { + + /** + * 前缀 + */ + String COUPON_PREFIX = "mall4cloud_biz:"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/CacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/CacheNames.java new file mode 100644 index 00000000..15abdffb --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/CacheNames.java @@ -0,0 +1,22 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * 缓存名字 + * + * @author FrozenWatermelon + * @date 2020/7/9 + */ +public interface CacheNames extends RbacCacheNames,OauthCacheNames,ProductCacheNames,MultishopCacheNames,PlatformCacheNames,BizCacheNames, UserCacheNames { + /** + * + * 参考CacheKeyPrefix + * cacheNames 与 key 之间的默认连接字符 + */ + String UNION = "::"; + + /** + * key内部的连接字符(自定义) + */ + String UNION_KEY = ":"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ConfigCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ConfigCacheNames.java new file mode 100644 index 00000000..77ed64bd --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ConfigCacheNames.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author lhd + * @date 2020/12/28 + */ +public interface ConfigCacheNames { + + /** + * 缓存配置名称前缀 + */ + String SYS_CONFIG = "sys_config:"; + + /** + * 缓存配置对象前缀 + */ + String SYS_CONFIG_OBJECT = SYS_CONFIG + "object:"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/MultishopCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/MultishopCacheNames.java new file mode 100644 index 00000000..a1efe1b7 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/MultishopCacheNames.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface MultishopCacheNames { + + /** + * 前缀 + */ + String MULTISHOP_PREFIX = "mall4cloud_multishop:"; + + /** + * 店铺分类列表缓存key + */ + String SHOP_DETAIL_ID_KEY = MULTISHOP_PREFIX + "shop_detail:getById:"; + + /** + * 店铺分类列表缓存key + */ + String INDEX_IMG_KEY = MULTISHOP_PREFIX + "index_img"; + + /** + * 店铺分类列表缓存key + */ + String NOTICES_KEY = MULTISHOP_PREFIX + "notices"; + + + String MULTISHOP_SIMPLE_INFO_KEY = MULTISHOP_PREFIX + "simple_info"; + + + String HOT_SEARCH_LIST_KEY = MULTISHOP_PREFIX + "hot_search_list"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OauthCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OauthCacheNames.java new file mode 100644 index 00000000..ddd36255 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OauthCacheNames.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface OauthCacheNames { + + /** + * oauth 授权相关key + */ + String OAUTH_PREFIX = "mall4cloud_oauth:"; + + /** + * token 授权相关key + */ + String OAUTH_TOKEN_PREFIX = OAUTH_PREFIX + "token:"; + + /** + * 保存token 缓存使用key + */ + String ACCESS = OAUTH_TOKEN_PREFIX + "access:"; + + /** + * 刷新token 缓存使用key + */ + String REFRESH_TO_ACCESS = OAUTH_TOKEN_PREFIX + "refresh_to_access:"; + + /** + * 根据uid获取保存的token key缓存使用的key + */ + String UID_TO_ACCESS = OAUTH_TOKEN_PREFIX + "uid_to_access:"; +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OrderCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OrderCacheNames.java new file mode 100644 index 00000000..ffd9d91b --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/OrderCacheNames.java @@ -0,0 +1,23 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface OrderCacheNames { + + /** + * 前缀 + */ + String ORDER_PREFIX = "mall4cloud_order:"; + + /** + * 确认订单信息缓存 + */ + String ORDER_CONFIRM_KEY = ORDER_PREFIX + "order:confirm"; + + /** + * 订单uuid + */ + String ORDER_CONFIRM_UUID_KEY = ORDER_PREFIX + "order:uuid_confirm"; +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/PlatformCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/PlatformCacheNames.java new file mode 100644 index 00000000..36f935c6 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/PlatformCacheNames.java @@ -0,0 +1,17 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface PlatformCacheNames { + + /** + * 前缀 + */ + String PLATFORM_PREFIX = "mall4cloud_platform:"; + + + String PLATFORM_SIMPLE_INFO_KEY = PLATFORM_PREFIX + "simple_info"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ProductCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ProductCacheNames.java new file mode 100644 index 00000000..7c43dcb5 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/ProductCacheNames.java @@ -0,0 +1,75 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface ProductCacheNames { + + /** + * 前缀 + */ + String PRODUCT_PREFIX = "mall4cloud_product:"; + + /** + * 根据skuid获取sku的缓存key + */ + String SKU_BY_ID_KEY = PRODUCT_PREFIX + "sku:by_id:"; + + /** + * 用户端分类列表缓存key + */ + String CATEGORY_LIST = PRODUCT_PREFIX + "category_list:"; + + /** + * 根据店铺id和上级id,获取分类列表缓存key + */ + String CATEGORY_LIST_BY_PARENT_ID_AND_SHOP_ID = PRODUCT_PREFIX + "category_list:shopIdAndParentId:"; + + /** + * 根据店铺id和上级id,获取分类列表缓存key + */ + String CATEGORY_LIST_ALL_OF_SHOP = PRODUCT_PREFIX + "category_list_all_of_shop:shopId:"; + + /** + * 分类下的属性列表缓存key + */ + String ATTRS_BY_CATEGORY_KEY = PRODUCT_PREFIX + "attrs_by_category:list:"; + + /** + * 分类下的品牌列表缓存key + */ + String BRAND_LIST_BY_CATEGORY = PRODUCT_PREFIX + "brand_list_by_category:id:"; + + /** + * 购物车商品数量 + */ + String SHOP_CART_ITEM_COUNT = "shop_cart:count:"; + + /** + * spu信息缓存key + */ + String SPU_KEY = PRODUCT_PREFIX + "spu:"; + + /** + * spu扩展信息缓存key + */ + String SPU_EXTENSION_KEY = PRODUCT_PREFIX + "spu_extension:"; + + /** + * sku列表信息缓存key + */ + String SKU_LIST_KEY = PRODUCT_PREFIX + "sku_list:"; + + /** + * 商品详情sku列表信息缓存key + */ + String SKU_OF_SPU_DETAIL_KEY = PRODUCT_PREFIX + "sku_of_spu_detail_list:"; + + /** + * 置顶品牌列表信息缓存key + */ + String BRAND_TOP = PRODUCT_PREFIX + "brand_top:list"; + + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/RbacCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/RbacCacheNames.java new file mode 100644 index 00000000..9ef80760 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/RbacCacheNames.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface RbacCacheNames { + + + /** + * 前缀 + */ + String RBAC_PREFIX = "mall4cloud_rbac:"; + + /** + * 所有权限列表缓存key + */ + String PERMISSIONS_KEY = RBAC_PREFIX + RBAC_PREFIX + "permission:permissions:"; + + /** + * 用户拥有的权限列表缓存key + */ + String USER_PERMISSIONS_KEY = RBAC_PREFIX + "permission:user_permissions:"; + + /** + * uri对应的权限缓存key + */ + String URI_PERMISSION_KEY = RBAC_PREFIX + "permission:uri_permissions:"; + + /** + * uri对应的权限缓存key + */ + String MENU_LIST_KEY = RBAC_PREFIX + "menu:list:"; + + /** + * 菜单id key + */ + String MENU_ID_LIST_KEY = RBAC_PREFIX + "menu:id_list:"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/UserCacheNames.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/UserCacheNames.java new file mode 100644 index 00000000..671493f7 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/constant/UserCacheNames.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.common.cache.constant; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +public interface UserCacheNames { + + /** + * 前缀 + */ + String USER_PREFIX = "mall4cloud_user:"; + + /** + * 用户信息缓存key + */ + String USER_INFO = USER_PREFIX + "info:"; + + /** + * 用户默认地址缓存key + */ + String USER_DEFAULT_ADDR = USER_PREFIX + "user_addr:user_id:"; + + /** + * 店铺分类列表缓存key + */ + String AREA_INFO_KEY = USER_PREFIX + "area_info"; + + /** + * 店铺分类列表缓存key + */ + String AREA_KEY = USER_PREFIX + "area"; + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/CacheManagerUtil.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/CacheManagerUtil.java new file mode 100644 index 00000000..d9622073 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/CacheManagerUtil.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.cache.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +@Component +public class CacheManagerUtil { + + private final CacheManager cacheManager; + + @Autowired + public CacheManagerUtil(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @SuppressWarnings({ "unchecked" }) + public T getCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) { + return null; + } + Cache.ValueWrapper valueWrapper = cache.get(key); + if (valueWrapper == null) { + return null; + } + return (T) valueWrapper.get(); + } + + public void putCache(String cacheName, String key, Object value) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.put(key, value); + } + } + + public void evictCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.evict(key); + } + } + +} diff --git a/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/RedisUtil.java b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/RedisUtil.java new file mode 100644 index 00000000..4cee055c --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-cache/src/main/java/com/mall4j/cloud/common/cache/util/RedisUtil.java @@ -0,0 +1,275 @@ +package com.mall4j.cloud.common.cache.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.util.SpringContextUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author FrozenWatermelon + * @date 2020/7/11 + */ +public class RedisUtil { + + private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class); + + @SuppressWarnings("unchecked") + private static final RedisTemplate REDIS_TEMPLATE = SpringContextUtils.getBean("redisTemplate", + RedisTemplate.class); + + public static final StringRedisTemplate STRING_REDIS_TEMPLATE = SpringContextUtils.getBean("stringRedisTemplate", + StringRedisTemplate.class); + + // =============================common============================ + /** + * 指定缓存失效时间 + * @param key 键 + * @param time 时间(秒) + * @return 是否成功 + */ + public static Boolean expire(String key, long time) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + try { + if (time > 0) { + REDIS_TEMPLATE.expire(key, time, TimeUnit.SECONDS); + } + return Boolean.TRUE; + } + catch (Exception e) { + logger.error("Set expire error: {}", e.getMessage()); + return Boolean.FALSE; + } + } + + /** + * 根据key 获取过期时间 + * @param key 键 不能为null + * @return 时间(秒) 返回-1代表为永久有效 失效时间为0,说明该主键未设置失效时间(失效时间默认为-1) + */ + public static Long getExpire(String key) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + return REDIS_TEMPLATE.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * @param key 键 + * @return true 存在 false 不存在 + */ + public static Boolean hasKey(String key) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + try { + return REDIS_TEMPLATE.hasKey(key); + } + catch (Exception e) { + logger.error("Error getting hasKey: {}", e.getMessage()); + return Boolean.FALSE; + } + } + + /** + * 删除缓存 + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public static void del(String... key) { + if (key != null && key.length > 0) { + for (String s : key) { + if (s.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + + if (key.length == 1) { + REDIS_TEMPLATE.delete(key[0]); + } + else { + REDIS_TEMPLATE.delete(Arrays.asList(key)); + } + } + } + + // ============================String============================= + /** + * 普通缓存获取 + * @param key 键 + * @return 值 + */ + @SuppressWarnings("unchecked") + public static T get(String key) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + return (T) REDIS_TEMPLATE.opsForValue().get(key); + } + + /** + * 普通缓存放入并设置时间 + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public static boolean set(String key, Object value, long time) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + try { + if (time > 0) { + REDIS_TEMPLATE.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } + else { + REDIS_TEMPLATE.opsForValue().set(key, value); + } + return true; + } + catch (Exception e) { + logger.error("Redis opsForValue error: {}", e.getMessage()); + return false; + } + } + + /** + * 递增 此时value值必须为int类型 否则报错 + * @param key 键 + * @param delta 要增加几(大于0) + * @return 自增后的值 + */ + public static Long incr(String key, long delta) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return STRING_REDIS_TEMPLATE.opsForValue().increment(key, delta); + } + + /** + * 递减 + * @param key 键 + * @param delta 要减少几(小于0) + * @return 自减后的值 + */ + public static Long decr(String key, long delta) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + if (delta < 0) { + throw new RuntimeException("递减因子必须小于0"); + } + return STRING_REDIS_TEMPLATE.opsForValue().increment(key, -delta); + } + + public static boolean setLongValue(String key, Long value, long time) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + try { + if (time > 0) { + STRING_REDIS_TEMPLATE.opsForValue().set(key, String.valueOf(value), time, TimeUnit.SECONDS); + } + else { + STRING_REDIS_TEMPLATE.opsForValue().set(key, String.valueOf(value)); + } + return true; + } + catch (Exception e) { + logger.error("setLongValue() error: {}", e.getMessage()); + return false; + } + } + + /** + * 普通缓存获取 + * @param key 键 + * @return 值 + */ + public static Long getLongValue(String key) { + if (key == null) { + return null; + } + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + String result = STRING_REDIS_TEMPLATE.opsForValue().get(key); + if (result == null) { + return null; + } + return Long.valueOf(result); + } + + /** + * 批量删除缓存 + * @param keys + */ + public static void deleteBatch(List keys) { + if (CollUtil.isEmpty(keys)) { + return; + } + for (String key : keys) { + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + REDIS_TEMPLATE.delete(keys); + } + + /** + * 批量删除缓存 + * @param cacheName 缓存名 + * @param cacheKeys 缓存key + */ + public static void deleteBatch(String cacheName, List cacheKeys) { + if (StrUtil.isBlank(cacheName) || CollUtil.isEmpty(cacheKeys)) { + return; + } + List strCacheKeys = cacheKeys.stream().map(String::valueOf).collect(Collectors.toList()); + List keys = new ArrayList<>(); + for (String cacheKey : strCacheKeys) { + String key = cacheName + CacheNames.UNION + cacheKey; + keys.add(key); + if (key.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + REDIS_TEMPLATE.delete(keys); + } + + /** + * 比较和删除标记,原子性 + * @return 是否成功 + */ + public static boolean cad(String key, String value) { + + if (key.contains(StrUtil.SPACE) || value.contains(StrUtil.SPACE)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + + String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + + //通过lure脚本原子验证令牌和删除令牌 + Long result = STRING_REDIS_TEMPLATE.execute(new DefaultRedisScript(script, Long.class), + Collections.singletonList(key), + value); + + return !Objects.equals(result, 0L); + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/lib/canal-glue-core.jar b/mall4cloud-common/mall4cloud-common-core/lib/canal-glue-core.jar new file mode 100644 index 00000000..96676572 Binary files /dev/null and b/mall4cloud-common/mall4cloud-common-core/lib/canal-glue-core.jar differ diff --git a/mall4cloud-common/mall4cloud-common-core/pom.xml b/mall4cloud-common/mall4cloud-common-core/pom.xml new file mode 100644 index 00000000..ffbef84f --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/pom.xml @@ -0,0 +1,83 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-core + mall4cloud 公共模块核心(公共中的公共代码) + jar + + + + + org.springframework.cloud + spring-cloud-context + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-json + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.github.openfeign + feign-httpclient + + + cn.hutool + hutool-core + + + cn.hutool + hutool-crypto + + + cn.hutool + hutool-http + + + org.jsoup + jsoup + + + ma.glasnost.orika + orika-core + + + com.github.xiaoymin + knife4j-micro-spring-boot-starter + + + cn.throwx + canal-glue-core + 1.0 + system + ${pom.basedir}/lib/canal-glue-core.jar + + + io.seata + seata-spring-boot-starter + + + + diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/AopConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/AopConfig.java new file mode 100644 index 00000000..a7451ef7 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/AopConfig.java @@ -0,0 +1,13 @@ +package com.mall4j.cloud.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * @author FrozenWatermelon + * @date 2020/9/21 + */ +@EnableAspectJAutoProxy(exposeProxy = true) +@Configuration +public class AopConfig { +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/OrikaConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/OrikaConfig.java new file mode 100644 index 00000000..6da4f688 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/OrikaConfig.java @@ -0,0 +1,28 @@ +package com.mall4j.cloud.common.config; + +import ma.glasnost.orika.MapperFacade; +import ma.glasnost.orika.MapperFactory; +import ma.glasnost.orika.impl.DefaultMapperFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * MapperFacade 用于dto ->entity的转换 + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@Configuration +public class OrikaConfig { + + @Bean + public MapperFactory mapperFactory() { + return new DefaultMapperFactory.Builder().build(); + } + + @Bean + public MapperFacade mapperFacade() { + return mapperFactory().getMapperFacade(); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/RestTemplateConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/RestTemplateConfig.java new file mode 100644 index 00000000..88eb51fe --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/config/RestTemplateConfig.java @@ -0,0 +1,21 @@ +package com.mall4j.cloud.common.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@Configuration +public class RestTemplateConfig { + + @Bean + @ConditionalOnMissingBean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Auth.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Auth.java new file mode 100644 index 00000000..a56164a4 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Auth.java @@ -0,0 +1,14 @@ +package com.mall4j.cloud.common.constant; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +public interface Auth { + + String CHECK_TOKEN_URI = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/token/checkToken"; + + String CHECK_RBAC_URI = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/permission/checkPermission"; +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Constant.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Constant.java new file mode 100644 index 00000000..5d0be869 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/Constant.java @@ -0,0 +1,85 @@ +package com.mall4j.cloud.common.constant; + +/** + * 常量 + * @author FrozenWatermelon + */ +public class Constant { + + /** 超级管理员ID */ + public static final int SUPER_ADMIN_ID = 1; + + /** + * 自营店id + */ + public static final long MAIN_SHOP = 1L; + + /** + * 如果把平台的数据也保存在店铺里面,如分类,热搜之类的,保存的店铺id + */ + public static final long PLATFORM_SHOP_ID = 0L; + + /** + * 商城缺失sku属性时的字符描述 + */ + public static final String SKU_PREFIX = "规格:"; + public static final String DEFAULT_SKU = "规格"; + + /** 系统菜单最大id */ + public static final int SYS_MENU_MAX_ID = 30; + + /** + * 查询订单成功状态 + */ + public static final String SUCCESS = "SUCCESS"; + + /** + * 一级分类id + */ + public static final Long CATEGORY_ID = 0L; + + /** + * 分类间隔 + */ + public static final String CATEGORY_INTERVAL = "-"; + + /** + * 店铺收藏返回的商品数量 + */ + public static final Integer SPU_SIZE_FIVE = 5; + + /** + * 订单如果未支付,30分钟后取消 + */ + public static final int ORDER_CANCEL_TIME = 60 * 1000 * 30; + + /** + * 如果因为网络原因,导致无法清楚订单是否已经下单成功,那么处理的时间应该要比订单取消时间晚一点 + */ + public static final int ORDER_CANCEL_WAIT_TIME = ORDER_CANCEL_TIME + 60 * 1000 * 10; + + /** + * 默认金额 + */ + public static final Long DEFAULT_AMOUNT = 0L; + + /** + * 字符串最大长度限制 + */ + public static final Integer MAX_FIELD_LIMIT = 500; + + /** + * 句号(英文符号) + */ + public static final String PERIOD = "."; + + /** + * 下划线 + */ + public static final String UNDERLINE = "_"; + + /** + * 逗号 + */ + public static final String COMMA = ","; +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/StatusEnum.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/StatusEnum.java new file mode 100644 index 00000000..b78841f5 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/StatusEnum.java @@ -0,0 +1,56 @@ +package com.mall4j.cloud.common.constant; + +/** + * 等级 + * @author yxf + * @date 2020/11/20 + */ +public enum StatusEnum { + + /** + * 删除 (逻辑删除) + */ + DELETE(-1), + + /** + * 禁用/过期/下架 + */ + DISABLE(0), + + /** + * 启用/未过期/上架 + */ + ENABLE(1), + + /** + * 违规下架 + */ + OFFLINE(2), + + /** + * 等待审核 + */ + WAIT_AUDIT(3) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + StatusEnum(Integer value) { + this.value = value; + } + + public static Boolean offlineStatus (Integer value) { + StatusEnum[] enums = values(); + for (StatusEnum statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return Boolean.TRUE; + } + } + return Boolean.FALSE; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/UserAdminType.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/UserAdminType.java new file mode 100644 index 00000000..0a6f277f --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/constant/UserAdminType.java @@ -0,0 +1,43 @@ +package com.mall4j.cloud.common.constant; + + +/** + * @author lhd + * @date 2020/12/30 + */ +public enum UserAdminType { + /** + * 管理员 + */ + ADMIN(1), + /** + * 用户 + */ + USER(0), + ; + + private Integer value; + + public Integer value() { + return value; + } + + public Integer getValue() { + return value; + } + + + UserAdminType(Integer value) { + this.value = value; + } + + public static UserAdminType instance(Integer value) { + UserAdminType[] enums = values(); + for (UserAdminType statusEnum : enums) { + if (statusEnum.getValue().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/BaseDTO.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/BaseDTO.java new file mode 100644 index 00000000..c773f3a4 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/BaseDTO.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.dto; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * 阿里java开发手册: 【强制】表必备三字段:id, create_time, update_time。 说明:其中 id 必为主键,类型为 bigint + * unsigned、单表时自增、步长为 1。create_time, update_time 的类型均为 datetime + * 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。 + * + * @author FrozenWatermelon + */ +public class BaseDTO { + + /** + * 创建时间 + */ + @ApiModelProperty("创建时间") + protected Date createTime; + + /** + * 更新时间 + */ + @ApiModelProperty("更新时间") + protected Date updateTime; + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "BaseDTO{" + "createTime=" + createTime + ", updateTime=" + updateTime + '}'; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/OrderSearchDTO.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/OrderSearchDTO.java new file mode 100644 index 00000000..4c3399a0 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/dto/OrderSearchDTO.java @@ -0,0 +1,195 @@ +package com.mall4j.cloud.common.dto; + +import io.swagger.annotations.ApiModelProperty; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +/** + * @author FrozenWatermelon + */ +public class OrderSearchDTO { + + /** + * 用户id + */ + private Long userId; + /** + * 店铺id + */ + private Long shopId; + + @ApiModelProperty("订单状态") + private Integer status; + + @ApiModelProperty("是否已经支付,1:已经支付过,0:没有支付过") + private Integer isPayed; + + /** + * 订购流水号 + */ + @ApiModelProperty("订单号") + private Long orderId; + + @ApiModelProperty("下单的时间范围:开始时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @ApiModelProperty("下单的时间范围:结束时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("商品名称") + private String spuName; + + @ApiModelProperty("收货人姓名") + private String consignee; + + @ApiModelProperty("收货人手机号") + private String mobile; + + @ApiModelProperty("物流类型 3:无需快递") + private Integer deliveryType; + + @ApiModelProperty("开始页") + private Integer pageNum; + + @ApiModelProperty("每页大小") + private Integer pageSize; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + @Override + public String toString() { + return "OrderSearchDTO{" + + "userId=" + userId + + ", shopId=" + shopId + + ", status=" + status + + ", isPayed=" + isPayed + + ", orderId=" + orderId + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", shopName='" + shopName + '\'' + + ", spuName='" + spuName + '\'' + + ", consignee='" + consignee + '\'' + + ", mobile='" + mobile + '\'' + + ", deliveryType=" + deliveryType + + ", pageNum=" + pageNum + + ", pageSize=" + pageSize + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/exception/mall4cloudException.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/exception/mall4cloudException.java new file mode 100644 index 00000000..eec31ec1 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/exception/mall4cloudException.java @@ -0,0 +1,51 @@ +package com.mall4j.cloud.common.exception; + +import com.mall4j.cloud.common.response.ResponseEnum; + +/** + * @author FrozenWatermelon + * @date 2020/7/11 + */ +public class mall4cloudException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private Object object; + + private ResponseEnum responseEnum; + + public mall4cloudException(String msg) { + super(msg); + } + + public mall4cloudException(String msg, Object object) { + super(msg); + this.object = object; + } + + public mall4cloudException(String msg, Throwable cause) { + super(msg, cause); + } + + + public mall4cloudException(ResponseEnum responseEnum) { + super(responseEnum.getMsg()); + this.responseEnum = responseEnum; + } + + public mall4cloudException(ResponseEnum responseEnum, Object object) { + super(responseEnum.getMsg()); + this.responseEnum = responseEnum; + this.object = object; + } + + + public Object getObject() { + return object; + } + + public ResponseEnum getResponseEnum() { + return responseEnum; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignBasicAuthRequestInterceptor.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignBasicAuthRequestInterceptor.java new file mode 100644 index 00000000..9ae49cd7 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignBasicAuthRequestInterceptor.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.common.feign; + +import cn.hutool.core.util.StrUtil; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author FrozenWatermelon + * @date 2020/11/27 + */ +@Component +@ConditionalOnClass({RequestInterceptor.class}) +public class FeignBasicAuthRequestInterceptor implements RequestInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(FeignBasicAuthRequestInterceptor.class); + + @Autowired + private FeignInsideAuthConfig feignInsideAuthConfig; + + @Override + public void apply(RequestTemplate template) { + // feign的内部请求,往请求头放入key 和 secret进行校验 + template.header(feignInsideAuthConfig.getKey(), feignInsideAuthConfig.getSecret()); + + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder + .getRequestAttributes(); + if (attributes == null) { + return; + } + HttpServletRequest request = attributes.getRequest(); + String authorization = request.getHeader("Authorization"); + + + if (StrUtil.isNotBlank(authorization)) { + template.header("Authorization", authorization); + } + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignHttpClientConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignHttpClientConfig.java new file mode 100644 index 00000000..4819a1b0 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignHttpClientConfig.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.common.feign; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author FrozenWatermelon + * @date 2020/12/11 + */ +@Configuration +public class FeignHttpClientConfig { + + @Bean(destroyMethod = "close") + public CloseableHttpClient httpClient() { + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setMaxTotal(400); + connectionManager.setDefaultMaxPerRoute(100); + + RequestConfig requestConfig = RequestConfig.custom() + //从连接池获取连接等待超时时间 + .setConnectionRequestTimeout(2000) + //请求超时时间 + .setConnectTimeout(2000) + //等待服务响应超时时间 + .setSocketTimeout(15000) + .build(); + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .evictExpiredConnections(); + return httpClientBuilder.build(); + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignInsideAuthConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignInsideAuthConfig.java new file mode 100644 index 00000000..5ea620f1 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/feign/FeignInsideAuthConfig.java @@ -0,0 +1,65 @@ +package com.mall4j.cloud.common.feign; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/9/10 + */ +@RefreshScope +@Configuration +@ConfigurationProperties("feign.inside") +public class FeignInsideAuthConfig { + + /** + * feign请求前缀 + */ + public static final String FEIGN_INSIDE_URL_PREFIX = "/feign"; + + @Value("${feign.inside.key}") + private String key; + + @Value("${feign.inside.secret}") + private String secret; + + @Value("#{'${feign.inside.ips:}'.split(',')}") + private List ips; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public List getIps() { + return ips; + } + + public void setIps(List ips) { + this.ips = ips; + } + + @Override + public String toString() { + return "FeignInsideAuthConfig{" + + "key='" + key + '\'' + + ", secret='" + secret + '\'' + + ", ips=" + ips + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/DefaultExceptionHandlerConfig.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/DefaultExceptionHandlerConfig.java new file mode 100644 index 00000000..8843b395 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/DefaultExceptionHandlerConfig.java @@ -0,0 +1,90 @@ +package com.mall4j.cloud.common.handler; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.seata.core.context.RootContext; +import io.seata.core.exception.TransactionException; +import io.seata.tm.api.GlobalTransactionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.ArrayList; +import java.util.List; + +/** + * 自定义错误处理器,除了授权只要请求服务器成功,就返回200,错误根据错误码前端进行处理 + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@RestController +@RestControllerAdvice +public class DefaultExceptionHandlerConfig { + + private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionHandlerConfig.class); + + @ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class }) + public ResponseEntity>> methodArgumentNotValidExceptionHandler(Exception e) { + logger.error("methodArgumentNotValidExceptionHandler", e); + List fieldErrors = null; + if (e instanceof MethodArgumentNotValidException) { + fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors(); + } + if (e instanceof BindException) { + fieldErrors = ((BindException) e).getBindingResult().getFieldErrors(); + } + if (fieldErrors == null) { + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID)); + } + + List defaultMessages = new ArrayList<>(fieldErrors.size()); + for (FieldError fieldError : fieldErrors) { + defaultMessages.add(fieldError.getField() + ":" + fieldError.getDefaultMessage()); + } + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, defaultMessages)); + } + + @ExceptionHandler({ HttpMessageNotReadableException.class }) + public ResponseEntity>> methodArgumentNotValidExceptionHandler( + HttpMessageNotReadableException e) { + logger.error("methodArgumentNotValidExceptionHandler", e); + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.HTTP_MESSAGE_NOT_READABLE)); + } + + @ExceptionHandler(mall4cloudException.class) + public ResponseEntity> mall4cloudExceptionHandler(mall4cloudException e) { + logger.error("mall4cloudExceptionHandler", e); + + ResponseEnum responseEnum = e.getResponseEnum(); + // 失败返回失败消息 + 状态码 + if (responseEnum != null) { + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(responseEnum, e.getObject())); + } + // 失败返回消息 状态码固定为直接显示消息的状态码 + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.showFailMsg(e.getMessage())); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> exceptionHandler(Exception e) throws TransactionException { + logger.error("exceptionHandler", e); + logger.info("RootContext.getXID(): " + RootContext.getXID()); + if (StrUtil.isNotBlank(RootContext.getXID())) { + GlobalTransactionContext.reload(RootContext.getXID()).rollback(); + } + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(ResponseEnum.EXCEPTION)); + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/HttpHandler.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/HttpHandler.java new file mode 100644 index 00000000..a5290cbf --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/handler/HttpHandler.java @@ -0,0 +1,62 @@ +package com.mall4j.cloud.common.handler; + +import cn.hutool.core.util.CharsetUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.xss.XssUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author FrozenWatermelon + * @date 2020/7/16 + */ +@Component +public class HttpHandler { + + private static final Logger logger = LoggerFactory.getLogger(HttpHandler.class); + + @Autowired + private ObjectMapper objectMapper; + + public void printServerResponseToWeb(ServerResponseEntity serverResponseEntity) { + if (serverResponseEntity == null) { + logger.info("print obj is null"); + return; + } + + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder + .getRequestAttributes(); + if (requestAttributes == null) { + logger.error("requestAttributes is null, can not print to web"); + return; + } + HttpServletResponse response = requestAttributes.getResponse(); + if (response == null) { + logger.error("httpServletResponse is null, can not print to web"); + return; + } + logger.error("response error: " + serverResponseEntity.getMsg()); + response.setCharacterEncoding(CharsetUtil.UTF_8); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + PrintWriter printWriter = null; + try { + printWriter = response.getWriter(); + printWriter.write(XssUtil.clean(objectMapper.writeValueAsString(serverResponseEntity))); + } + catch (IOException e) { + throw new mall4cloudException("io 异常", e); + } + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/model/BaseModel.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/model/BaseModel.java new file mode 100644 index 00000000..aae932ba --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/model/BaseModel.java @@ -0,0 +1,46 @@ +package com.mall4j.cloud.common.model; + +import java.io.Serializable; +import java.util.Date; + +/** + * 阿里java开发手册: 【强制】表必备三字段:id, create_time, update_time。 说明:其中 id 必为主键,类型为 bigint + * unsigned、单表时自增、步长为 1。create_time, update_time 的类型均为 datetime + * 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。 + * + * @author FrozenWatermelon + */ +public class BaseModel implements Serializable { + + /** + * 创建时间 + */ + protected Date createTime; + + /** + * 更新时间 + */ + protected Date updateTime; + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "BaseModel{" + "createTime=" + createTime + ", updateTime=" + updateTime + '}'; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ResponseEnum.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ResponseEnum.java new file mode 100644 index 00000000..38bd4c69 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ResponseEnum.java @@ -0,0 +1,165 @@ +package com.mall4j.cloud.common.response; + +/** + * @author FrozenWatermelon + * @date 2020/7/9 + */ +public enum ResponseEnum { + + /** + * ok + */ + OK("00000", "ok"), + + /** + * 用于直接显示提示用户的错误,内容由输入内容决定 + */ + SHOW_FAIL("A00001", ""), + + /** + * 方法参数没有校验,内容由输入内容决定 + */ + METHOD_ARGUMENT_NOT_VALID("A00002", ""), + + /** + * 无法读取获取请求参数 + */ + HTTP_MESSAGE_NOT_READABLE("A00003", "请求参数格式有误"), + + /** + * 未授权 + */ + UNAUTHORIZED("A00004", "Unauthorized"), + + /** + * 服务器出了点小差 + */ + EXCEPTION("A00005", "服务器出了点小差"), + + /** + * 数据异常 + */ + DATA_ERROR("A00007", "数据异常,请刷新后重新操作"), + + /** + * 一些需要登录的接口,而实际上因为前端无法知道token是否已过期,导致token已失效时, + * 应该返回一个状态码,告诉前端token已经失效了,及时清理 + */ + CLEAN_TOKEN("A00008", "clean token"), + + /** + * 刷新token已过期 + */ + REFRESH_TOKEN_EXIST("A00009", "refresh token exist"), + + /** + * 数据不完整 + */ + DATA_INCOMPLETE("A00010", "数据不完整"), + + /** + * 01开头代表商品 + */ + SPU_NOT_EXIST("A01000", "spu not exist"), + + /** + * 02开头代表购物车 + */ + SHOP_CART_NOT_EXIST("A02000", "shop cart not exist"), + + /** + * 03开头代表订单 + */ + ORDER_NOT_EXIST("A03000", "order not exist"), + + /** + * 请勿重复提交订单, + * 1.当前端遇到该异常时,说明前端防多次点击没做好 + * 2.提示用户 订单已发生改变,请勿重复下单 + */ + REPEAT_ORDER("A03002", "please don't repeat order"), + + /** + * 订单已过期,当前端看到该状态码的时候,提示订单信息已过期,请重新确认后提交,此时用户点击确定,前端刷新页面。 + */ + ORDER_EXPIRED("A03003", "order expired"), + + /** + * 订单已支付,无法取消订单 + */ + ORDER_PAYED("A03007", "order payed"), + + /** + * 订单未发货,无法确认收货 + */ + ORDER_NO_DELIVERY("A03008", "order no delivery"), + + /** + * 库存不足,body会具体返回那个skuid的库存不足 + */ + NOT_STOCK("A03010", "not stock"), + + /** + * 订单未完成或未关闭,无法删除订单 + */ + ORDER_NOT_FINISH_OR_CLOSE("A03011", "order not finish or close"), + + /** + * 订单未支付 + */ + ORDER_NOT_PAYED("A03012", "order not payed"), + + /** + * 订单已失败 + */ + ORDER_HAS_FAILED("A03013", "order has failed"), + + /** + * 没有查询权限 + */ + REFUND_NOT_PERMISSION("A03024", "refund not permission"), + + /** + * 撤销失败 当前状态不允许此操作 + */ + REFUND_STATUS_CHECK("A03034", "refund status check"), + + /** + * 04 开头代表注册登录之类的异常状态 + * 社交账号未绑定,当前端看到该异常时,应该在合适的时间(比如在购买的时候跳)根据社交账号的类型,跳转到绑定系统账号的页面 + */ + SOCIAL_ACCOUNT_NOT_BIND("A04001", "social account not bind"), + + /** + * 有些时候第三方系统授权之后,会有个临时的key,比如小程序的session_key + * 这个异常代表session_key过期,前端遇到这个问题的时候,应该再次调用社交登录的接口,刷新session_key + */ + BIZ_TEMP_SESSION_KEY_EXPIRE("A04002", "biz temp session key expire"), + + /** + * 账号未注册,前端看到这个状态码,弹出选择框,提示用户账号未注册,是否进入注册页面,用户选择是,进入注册页面 + */ + ACCOUNT_NOT_REGISTER("A04003", "account not register"); + private final String code; + + private final String msg; + + public String value() { + return code; + } + + public String getMsg() { + return msg; + } + + ResponseEnum(String code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String toString() { + return "ResponseEnum{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + "} " + super.toString(); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ServerResponseEntity.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ServerResponseEntity.java new file mode 100644 index 00000000..d826707d --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/response/ServerResponseEntity.java @@ -0,0 +1,119 @@ +package com.mall4j.cloud.common.response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 统一的返回数据 + * + * @author FrozenWatermelon + * @date 2020/7/3 + */ +public class ServerResponseEntity implements Serializable { + + private static final Logger log = LoggerFactory.getLogger(ServerResponseEntity.class); + + /** + * 状态码 + */ + private String code; + + /** + * 信息 + */ + private String msg; + + /** + * 数据 + */ + private T data; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public boolean isSuccess() { + return Objects.equals(ResponseEnum.OK.value(), this.code); + } + + @Override + public String toString() { + return "ServerResponseEntity{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; + } + + public static ServerResponseEntity success(T data) { + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setData(data); + serverResponseEntity.setCode(ResponseEnum.OK.value()); + return serverResponseEntity; + } + + public static ServerResponseEntity success() { + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setCode(ResponseEnum.OK.value()); + serverResponseEntity.setMsg(ResponseEnum.OK.getMsg()); + return serverResponseEntity; + } + + /** + * 前端显示失败消息 + * @param msg 失败消息 + * @return + */ + public static ServerResponseEntity showFailMsg(String msg) { + log.error(msg); + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setMsg(msg); + serverResponseEntity.setCode(ResponseEnum.SHOW_FAIL.value()); + return serverResponseEntity; + } + + public static ServerResponseEntity fail(ResponseEnum responseEnum) { + log.error(responseEnum.toString()); + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setMsg(responseEnum.getMsg()); + serverResponseEntity.setCode(responseEnum.value()); + return serverResponseEntity; + } + + public static ServerResponseEntity fail(ResponseEnum responseEnum, T data) { + log.error(responseEnum.toString()); + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setMsg(responseEnum.getMsg()); + serverResponseEntity.setCode(responseEnum.value()); + serverResponseEntity.setData(data); + return serverResponseEntity; + } + + public static ServerResponseEntity transform(ServerResponseEntity oldServerResponseEntity) { + ServerResponseEntity serverResponseEntity = new ServerResponseEntity<>(); + serverResponseEntity.setMsg(oldServerResponseEntity.getMsg()); + serverResponseEntity.setCode(oldServerResponseEntity.getCode()); + log.error(serverResponseEntity.toString()); + return serverResponseEntity; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/serializer/ImgJsonSerializer.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/serializer/ImgJsonSerializer.java new file mode 100644 index 00000000..7821c107 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/serializer/ImgJsonSerializer.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.serializer; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.mall4j.cloud.common.util.PrincipalUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +@Component +@RefreshScope +public class ImgJsonSerializer extends JsonSerializer { + + @Value("${biz.oss.resources-url}") + private String imgDomain; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (StrUtil.isBlank(value)) { + gen.writeString(StrUtil.EMPTY); + return; + } else if (StrUtil.isBlank(imgDomain)) { + gen.writeString(value); + return; + } + String[] imgs = value.split(StrUtil.COMMA); + StringBuilder sb = new StringBuilder(); + + for (String img : imgs) { + // 图片为http协议开头,直接返回 + if (PrincipalUtil.isHttpProtocol(img)) { + sb.append(img).append(StrUtil.COMMA); + } + else { + sb.append(imgDomain).append(img).append(StrUtil.COMMA); + } + } + sb.deleteCharAt(sb.length() - 1); + gen.writeString(sb.toString()); + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/BooleanUtil.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/BooleanUtil.java new file mode 100644 index 00000000..a3e55e3b --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/BooleanUtil.java @@ -0,0 +1,27 @@ +package com.mall4j.cloud.common.util; + +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/9/2 + */ +public class BooleanUtil { + + /** + * 判断一个数字是否为true(等于1就是true) + * @param num 输入的数字 + * @return 是否为true + */ + public static boolean isTrue(Integer num) { + if (num == null) { + return false; + } + return Objects.equals(num, 1); + } + + public static boolean isFalse(Integer num) { + return !isTrue(num); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/IpHelper.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/IpHelper.java new file mode 100644 index 00000000..70e41a4d --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/IpHelper.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.common.util; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * IP帮助工具 + * @author FrozenWatermelon + */ +public class IpHelper { + private static final String UNKNOWN = "unknown"; + + /** + * 得到用户的真实地址,如果有多个就取第一个 + * + * @return + */ + public static String getIpAddr() { + if (RequestContextHolder.getRequestAttributes() == null) { + return ""; + } + HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + String[] ips = ip.split(","); + return ips[0].trim(); + } + + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/Json.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/Json.java new file mode 100644 index 00000000..5e7aaa2c --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/Json.java @@ -0,0 +1,151 @@ +package com.mall4j.cloud.common.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/7/11 + */ +public class Json { + + private static final Logger logger = LoggerFactory.getLogger(Json.class); + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + static { + // 如果为空则不输出 + OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + // 对于空的对象转json的时候不抛出错误 + OBJECT_MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + // 禁用序列化日期为timestamps + OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + // 禁用遇到未知属性抛出异常 + OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + /** + * 对象转json + * @param object 对象 + * @return json + */ + public static String toJsonString(Object object) { + try { + return OBJECT_MAPPER.writeValueAsString(object); + } + catch (JsonProcessingException e) { + logger.error("toJsonString() error: {}", e.getMessage()); + } + return ""; + } + + /** + * json转换换成对象 + * @param json json + * @param clazz clazz + * @return Class + */ + public static T parseObject(String json, Class clazz) { + if (json == null) { + return null; + } + T result = null; + try { + result = OBJECT_MAPPER.readValue(json, clazz); + } + catch (Exception e) { + logger.error("parseObject() error: {}", e.getMessage()); + } + return result; + } + + /** + * json转换换成对象 + * @param src src + * @param clazz clazz + * @return Class + */ + public static T parseObject(byte[] src, Class clazz) { + T result = null; + try { + result = OBJECT_MAPPER.readValue(src, clazz); + } + catch (Exception e) { + logger.error("parseObject() error: {}", e.getMessage()); + } + return result; + } + + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + /** + * * + * https://stackoverflow.com/questions/6349421/how-to-use-jackson-to-deserialise-an-array-of-objects + * * List myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class)) + * * works up to 10 time faster than TypeRefence. + * @return List数组 + */ + public static List parseArray(String json, Class clazz) { + if (json == null) { + return null; + } + T[] result = null; + try { + result = OBJECT_MAPPER.readValue(json, clazz); + } + catch (Exception e) { + logger.error("parseArray() error: {}", e.getMessage()); + } + if (result == null) { + return Collections.emptyList(); + } + return Arrays.asList(result); + } + + public static List parseArray(byte[] src, Class clazz) { + T[] result = null; + try { + result = OBJECT_MAPPER.readValue(src, clazz); + } + catch (Exception e) { + logger.error("parseArray() error: {}", e.getMessage()); + } + if (result == null) { + return Collections.emptyList(); + } + return Arrays.asList(result); + } + + + /** + * 转换成json节点,即map + * @param jsonStr jsonStr + * @return JsonNode + */ + public static JsonNode parseJson(String jsonStr) { + if (jsonStr == null) { + return null; + } + JsonNode jsonNode = null; + try { + jsonNode = OBJECT_MAPPER.readTree(jsonStr); + } + catch (Exception e) { + logger.error("parseJson() error: {}", e.getMessage()); + } + return jsonNode; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/PrincipalUtil.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/PrincipalUtil.java new file mode 100644 index 00000000..a5ce63ca --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/PrincipalUtil.java @@ -0,0 +1,114 @@ +package com.mall4j.cloud.common.util; + +import cn.hutool.core.util.StrUtil; + +import java.util.regex.Pattern; + +/** + * 正则表达式工具 + * + * @author FrozenWatermelon + */ +public class PrincipalUtil { + + /** + * 以1开头,后面跟10位数 + */ + public static final String MOBILE_REGEXP = "1[0-9]{10}"; + + /** + * 1. 用户名不能为纯数字 2. 由数字字母下划线 4-16位组成 + */ + public static final String USER_NAME_REGEXP = "(?!\\d+$)([a-zA-Z0-9_]{4,16})"; + + /** + * 字段名,数字字母下划线 + */ + public static final String FIELD_REGEXP = "([a-zA-Z0-9_]+)"; + + /** + * 由简单的字母数字拼接而成的字符串 不含有下划线,大写字母 + */ + public static final String SIMPLE_CHAR_REGEXP = "([a-z0-9]+)"; + + /** + * 邮箱正则 + */ + public static final String EMAIL_REGEXP = "[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?"; + + /** + * http协议正则 + */ + public static final String HTTP_PROTOCOL_REGEXP = "^((http[s]{0,1})://)"; + + + + /** + * 是否是手机号 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isMobile(String value) { + return isMatching(MOBILE_REGEXP, value); + } + + /** + * 是否是用户名 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isUserName(String value) { + return isMatching(USER_NAME_REGEXP, value); + } + + /** + * 是否符合字段规则 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isField(String value) { + return !isMatching(FIELD_REGEXP, value); + } + + /** + * 是否是邮箱 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isEmail(String value) { + return isMatching(EMAIL_REGEXP, value); + } + + /** + * 是否是由简单的字母数字拼接而成的字符串 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isSimpleChar(String value) { + return isMatching(SIMPLE_CHAR_REGEXP, value); + } + + /** + * 是否是HTTP协议 + * @param value 输入值 + * @return 匹配结果 + */ + public static boolean isHttpProtocol(String value) { + return isFind(HTTP_PROTOCOL_REGEXP, value); + } + + public static boolean isMatching(String regexp, String value) { + if (StrUtil.isBlank(value)) { + return false; + } + return Pattern.matches(regexp, value); + } + + public static boolean isFind(String regexp, String value) { + if (StrUtil.isBlank(value)) { + return false; + } + Pattern pattern= Pattern.compile(regexp); + return pattern.matcher(value).find(); + } +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/SpringContextUtils.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/SpringContextUtils.java new file mode 100644 index 00000000..8ced383d --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/util/SpringContextUtils.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.util; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Context 工具类 + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + + public static ApplicationContext applicationContext; + + @SuppressWarnings("NullableProblems") + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + public static Object getBean(String name) { + return applicationContext.getBean(name); + } + + public static T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + public static T getBean(String name, Class requiredType) { + return applicationContext.getBean(name, requiredType); + } + + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + public static boolean isSingleton(String name) { + return applicationContext.isSingleton(name); + } + + public static Class getType(String name) { + return applicationContext.getType(name); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/vo/BaseVO.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/vo/BaseVO.java new file mode 100644 index 00000000..ad6b5e03 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/vo/BaseVO.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * 阿里java开发手册: 【强制】表必备三字段:id, create_time, update_time。 说明:其中 id 必为主键,类型为 bigint + * unsigned、单表时自增、步长为 1。create_time, update_time 的类型均为 datetime + * 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。 + * + * @author FrozenWatermelon + */ +public class BaseVO { + + /** + * 创建时间 + */ + @ApiModelProperty("创建时间") + protected Date createTime; + + /** + * 更新时间 + */ + @ApiModelProperty("更新时间") + protected Date updateTime; + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "BaseDTO{" + "createTime=" + createTime + ", updateTime=" + updateTime + '}'; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssUtil.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssUtil.java new file mode 100644 index 00000000..a9558fa0 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssUtil.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.common.xss; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.safety.Whitelist; + +/** + * 描述: 过滤 HTML 标签中 XSS 代码 + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +public class XssUtil { + + /** + * 使用自带的 basicWithImages 白名单 + */ + private static final Whitelist WHITE_LIST = Whitelist.relaxed(); + + /** 配置过滤化参数, 不对代码进行格式化 */ + private static final Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false); + static { + // 富文本编辑时一些样式是使用 style 来进行实现的 + // 比如红色字体 style="color:red;" + // 所以需要给所有标签添加 style 属性 + WHITE_LIST.addAttributes(":all", "style"); + } + + public static String clean(String content) { + return Jsoup.clean(content, "", WHITE_LIST, OUTPUT_SETTINGS); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssWrapper.java b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssWrapper.java new file mode 100644 index 00000000..9e47e009 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-core/src/main/java/com/mall4j/cloud/common/xss/XssWrapper.java @@ -0,0 +1,81 @@ +package com.mall4j.cloud.common.xss; + +import cn.hutool.core.util.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +/** + * xss 攻击过滤 + * + * @author FrozenWatermelon + */ +public class XssWrapper extends HttpServletRequestWrapper { + + /** + * Constructs a request object wrapping the given request. + * @param request The request to wrap + * @throws IllegalArgumentException if the request is null + */ + public XssWrapper(HttpServletRequest request) { + super(request); + } + + /** + * 对数组参数进行特殊字符过滤 + */ + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values == null) { + return null; + } + int count = values.length; + String[] encodedValues = new String[count]; + for (int i = 0; i < count; i++) { + encodedValues[i] = cleanXss(values[i]); + } + return encodedValues; + } + + /** + * 对参数中特殊字符进行过滤 + */ + @Override + public String getParameter(String name) { + String value = super.getParameter(name); + if (StrUtil.isBlank(value)) { + return value; + } + return cleanXss(value); + } + + /** + * 获取attribute,特殊字符过滤 + */ + @Override + public Object getAttribute(String name) { + Object value = super.getAttribute(name); + if (value instanceof String && StrUtil.isNotBlank((String) value)) { + return cleanXss((String) value); + } + return value; + } + + /** + * 对请求头部进行特殊字符过滤 + */ + @Override + public String getHeader(String name) { + String value = super.getHeader(name); + if (StrUtil.isBlank(value)) { + return value; + } + return cleanXss(value); + } + + private String cleanXss(String value) { + return XssUtil.clean(value); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-database/pom.xml b/mall4cloud-common/mall4cloud-common-database/pom.xml new file mode 100644 index 00000000..2449cbec --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/pom.xml @@ -0,0 +1,42 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-database + mall4cloud 数据库连接相关代码 + jar + + + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + mysql + mysql-connector-java + + + com.mall4j.cloud + mall4cloud-api-leaf + ${project.version} + + + + diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/annotations/DistributedId.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/annotations/DistributedId.java new file mode 100644 index 00000000..cd92e80c --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/annotations/DistributedId.java @@ -0,0 +1,22 @@ +package com.mall4j.cloud.common.database.annotations; + +import com.mall4j.cloud.common.database.interceptor.GeneratedKeyInterceptor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 分布式id标识 + * 如果一个类是model中的类,也就是继承BaseModel的类,并且字段含有该注解,当插入数据的时候,会往该字段插入分布式id + * @author FrozenWatermelon + * @date 2020/09/09 + * @see GeneratedKeyInterceptor + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DistributedId { + + String value(); +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/MybatisConfig.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/MybatisConfig.java new file mode 100644 index 00000000..8159f870 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/MybatisConfig.java @@ -0,0 +1,15 @@ +package com.mall4j.cloud.common.database.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +/** + * @author FrozenWatermelon + * @date 2020/6/24 + */ +@Configuration +@MapperScan({ "com.mall4j.cloud.**.mapper" }) +public class MybatisConfig { + + +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/SeataRequestInterceptor.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/SeataRequestInterceptor.java new file mode 100644 index 00000000..ebf95231 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/config/SeataRequestInterceptor.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.common.database.config; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.constant.Auth; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import io.seata.core.context.RootContext; +import io.seata.spring.annotation.GlobalTransactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.stereotype.Component; + +/** + * @author FrozenWatermelon + * @date 2020/11/27 + */ +@Component +@ConditionalOnClass({RequestInterceptor.class, GlobalTransactional.class}) +public class SeataRequestInterceptor implements RequestInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(SeataRequestInterceptor.class); + + @Override + public void apply(RequestTemplate template) { + String currentXid = RootContext.getXID(); + if (StrUtil.isNotBlank(currentXid) && !template.url().startsWith(Auth.CHECK_TOKEN_URI) && !template.url().startsWith(Auth.CHECK_RBAC_URI)) { + template.header(RootContext.KEY_XID, currentXid); + } + } +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/dto/PageDTO.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/dto/PageDTO.java new file mode 100644 index 00000000..b57c4a3f --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/dto/PageDTO.java @@ -0,0 +1,145 @@ +package com.mall4j.cloud.common.database.dto; + +import com.github.pagehelper.IPage; +import com.mall4j.cloud.common.util.PrincipalUtil; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +/** + * @author FrozenWatermelon + * @date 2020/9/8 + */ +public class PageDTO implements IPage { + + public static final String ASC = "ASC"; + + public static final String DESC = "DESC"; + + /** + * 最大分页大小,如果分页大小大于500,则用500作为分页的大小。防止有人直接传入一个较大的数,导致服务器内存溢出宕机 + */ + public static final Integer MAX_PAGE_SIZE = 500; + + /** + * 当前页 + */ + @NotNull(message = "pageNum 不能为空") + @ApiModelProperty(value = "当前页", required = true) + private Integer pageNum; + + @NotNull(message = "pageSize 不能为空") + @ApiModelProperty(value = "每页大小", required = true) + private Integer pageSize; + + @ApiModelProperty(value = "排序字段数组,用逗号分割") + private String[] columns; + + @ApiModelProperty(value = "排序字段方式,用逗号分割,ASC正序,DESC倒序") + private String[] orders; + + @Override + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + @Override + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + if (pageSize > MAX_PAGE_SIZE) { + this.pageSize = MAX_PAGE_SIZE; + return; + } + this.pageSize = pageSize; + } + + @Override + public String getOrderBy() { + return order(this.columns, this.orders); + } + + public String[] getColumns() { + return columns; + } + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public String[] getOrders() { + return orders; + } + + public void setOrders(String[] orders) { + this.orders = orders; + } + + public static String order(String[] columns, String[] orders) { + + if (columns == null || columns.length == 0) { + return ""; + } + + StringBuilder stringBuilder = new StringBuilder(); + + for (int x = 0; x < columns.length; x++) { + + String column = columns[x]; + String order; + + if (orders != null && orders.length > x) { + order = orders[x].toUpperCase(); + if (!(order.equals(ASC) || order.equals(DESC))) { + throw new IllegalArgumentException("非法的排序策略:" + column); + } + }else { + order = ASC; + } + + // 判断列名称的合法性,防止SQL注入。只能是【字母,数字,下划线】 + if (PrincipalUtil.isField(column)) { + throw new IllegalArgumentException("非法的排序字段名称:" + column); + } + + // 驼峰转换为下划线 + column = humpConversionUnderscore(column); + + if (x != 0) { + stringBuilder.append(", "); + } + stringBuilder.append("`").append(column).append("` ").append(order); + } + return stringBuilder.toString(); + } + + public static String humpConversionUnderscore(String value) { + StringBuilder stringBuilder = new StringBuilder(); + char[] chars = value.toCharArray(); + for (char character : chars) { + if (Character.isUpperCase(character)) { + stringBuilder.append("_"); + character = Character.toLowerCase(character); + } + stringBuilder.append(character); + } + return stringBuilder.toString(); + } + + @Override + public String toString() { + return "PageDTO{" + + "pageNum=" + pageNum + + ", pageSize=" + pageSize + + ", columns=" + Arrays.toString(columns) + + ", orders=" + Arrays.toString(orders) + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/interceptor/GeneratedKeyInterceptor.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/interceptor/GeneratedKeyInterceptor.java new file mode 100644 index 00000000..1155c29c --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/interceptor/GeneratedKeyInterceptor.java @@ -0,0 +1,165 @@ +package com.mall4j.cloud.common.database.interceptor; + +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.common.database.annotations.DistributedId; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.model.BaseModel; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + + +/** + * 分布式id生成 + * 1. 分布式id是通过美团的leaf生成的,是需要与mall4cloud-leaf数据库 当中 leaf_alloc表中 biz_tag字段相关联的key + * 2. 为了注入分布式id更加方便,规定为DistributedId为注解的字段加入该字段 + * @see DistributedId + * @author FrozenWatermelon + * @date 2020/9/9 + */ +@Component +@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) +public class GeneratedKeyInterceptor implements Interceptor { + + private static final Logger logger = LoggerFactory.getLogger(GeneratedKeyInterceptor.class); + /** + * 单个插入名称 + */ + private static final String INSERT = "insert"; + + /** + * 单个插入名称 + */ + private static final String SAVE = "save"; + + /** + * 批量插入名称 + */ + private static final String BATCH_INSERT = "insertBatch"; + + /** + * 批量插入名称 + */ + private static final String BATCH_SAVE = "saveBatch"; + + @Autowired + private SegmentFeignClient segmentFeignClient; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + + MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0]; + + // 获取 SQL + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + + // 不是 insert 类型的跳过 + if (SqlCommandType.INSERT != sqlCommandType) { + return invocation.proceed(); + } + + int one = 1; + + // 获取参数 + Object parameter = invocation.getArgs()[one]; + + // 找数据库中的对象 + Object dbObject = findDbObject(parameter); + + if (dbObject == null) { + return invocation.proceed(); + } + + // 插入 + if (mappedStatement.getId().contains(INSERT) || mappedStatement.getId().contains(SAVE)){ + generatedKey(dbObject); + } + // 批量插入 + else if (mappedStatement.getId().contains(BATCH_INSERT) || mappedStatement.getId().contains(BATCH_SAVE)){ + // 获取批量查询的参数并生成主键 + if (parameter instanceof HashMap){ + Object list = ((Map)parameter).get("list"); + if (list instanceof ArrayList) { + for (Object o : (ArrayList) list) { + generatedKey(dbObject); + } + } + } + } + + return invocation.proceed(); + } + + protected BaseModel findDbObject(Object parameterObj) { + if (parameterObj instanceof BaseModel) { + return (BaseModel)parameterObj; + } else if (parameterObj instanceof Map) { + for (Object val : ((Map) parameterObj).values()) { + if (val instanceof BaseModel) { + return (BaseModel)val; + } + } + } + return null; + } + + /** + * 获取私有成员变量 ,并设置主键 + * @param parameter 参数 + */ + private void generatedKey(Object parameter) throws Throwable { + + Field[] fieldList = parameter.getClass().getDeclaredFields(); + + for (Field field : fieldList) { + + if (!field.getType().isAssignableFrom(Long.class)) { + break; + } + + DistributedId annotation = field.getAnnotation(DistributedId.class); + if (annotation == null) { + break; + } + + field.setAccessible(true); + if (field.get(parameter) != null) { + break; + } + ServerResponseEntity segmentIdResponseEntity = segmentFeignClient.getSegmentId(annotation.value()); + if (segmentIdResponseEntity.isSuccess()) { + // 这里设置分布式id + field.set(parameter,segmentIdResponseEntity.getData()); + } else { + logger.error("can't get distributed id !!!! "); + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + } + + /** + * Plugin.wrap生成拦截代理对象 + */ + @Override + public Object plugin(Object o) { + if (o instanceof Executor) { + return Plugin.wrap(o, this); + } else { + return o; + } + } + +} + diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageAdapter.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageAdapter.java new file mode 100644 index 00000000..5152b21b --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageAdapter.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.database.util; + +import com.mall4j.cloud.common.database.dto.PageDTO; + + +/** + * 分页适配 + * @author FrozenWatermelon + */ +public class PageAdapter { + + private int begin; + + private int size; + + public PageAdapter(PageDTO page) { + this.begin = getStart(page.getPageNum() - 1, page.getPageSize()); + this.size = page.getPageSize(); + } + + public int getBegin() { + return begin; + } + + public void setBegin(int begin) { + this.begin = begin; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public static int getStart(int pageNo, int pageSize) { + if (pageNo < 0) { + pageNo = 0; + } + + if (pageSize < 1) { + pageSize = 0; + } + + return pageNo * pageSize; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageUtil.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageUtil.java new file mode 100644 index 00000000..72598fe8 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/util/PageUtil.java @@ -0,0 +1,41 @@ +package com.mall4j.cloud.common.database.util; + +import com.github.pagehelper.ISelect; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageSerializable; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; + +/** + * @author FrozenWatermelon + * @date 2020/9/8 + */ +public class PageUtil { + + /** + * 使用pagehelper进行分页,该分页只能一对一 + */ + public static PageVO doPage(PageDTO pageDTO, ISelect select) { + + PageSerializable simplePageInfo = PageHelper.startPage(pageDTO).doSelectPageSerializable(select); + + PageVO pageVO = new PageVO<>(); + pageVO.setList(simplePageInfo.getList()); + pageVO.setTotal(simplePageInfo.getTotal()); + pageVO.setPages(getPages(simplePageInfo.getTotal(), pageDTO.getPageSize())); + return pageVO; + } + + + + public static Integer getPages(long total, Integer pageSize) { + + if (total == -1) { + return 1; + } + if (pageSize > 0) { + return (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1)); + } + return 0; + } +} diff --git a/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/vo/PageVO.java b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/vo/PageVO.java new file mode 100644 index 00000000..5f9ebdc1 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-database/src/main/java/com/mall4j/cloud/common/database/vo/PageVO.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.common.database.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/9/8 + */ +public class PageVO { + + @ApiModelProperty("总页数") + private Integer pages; + + @ApiModelProperty("总条目数") + private Long total; + + @ApiModelProperty("结果集") + private List list; + + public Integer getPages() { + return pages; + } + + public void setPages(Integer pages) { + this.pages = pages; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + @Override + public String toString() { + return "PageVO{" + + ", pages=" + pages + + ", total=" + total + + ", list=" + list + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/pom.xml b/mall4cloud-common/mall4cloud-common-order/pom.xml new file mode 100644 index 00000000..7ade9686 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/pom.xml @@ -0,0 +1,25 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-order + jar + mall4cloud 订单相关公共类 + + + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/bo/PayNotifyBO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/bo/PayNotifyBO.java new file mode 100644 index 00000000..1e397b1f --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/bo/PayNotifyBO.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.common.order.bo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 订单支付成功通知 + * @author FrozenWatermelon + * @date 2020/12/8 + */ +public class PayNotifyBO { + + private List orderIds; + + public PayNotifyBO(){ + + } + + public PayNotifyBO(List orderIds) { + this.orderIds = orderIds; + } + + public List getOrderIds() { + return orderIds; + } + + public void setOrderIds(List orderIds) { + this.orderIds = orderIds; + } + + @Override + public String toString() { + return "PayNotifyBO{" + + "orderIds=" + orderIds + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/constant/OrderCloseType.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/constant/OrderCloseType.java new file mode 100644 index 00000000..c1d9faf2 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/constant/OrderCloseType.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.common.order.constant; + +/** + * 订单关闭 + * + * @author YXF + * * @date 2021-01-27 09:10:00 + */ +public enum OrderCloseType { + + /** + * 超时未支付 + */ + OVERTIME(1), + + /** + * 买家取消 + */ + BUYER(4), + + /** + * 已通过货到付款交易 + */ + DELIVERY(15), + ; + + private Integer code; + + public Integer value() { + return code; + } + + OrderCloseType(Integer code) { + this.code = code; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartItemVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartItemVO.java new file mode 100644 index 00000000..76e521f5 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartItemVO.java @@ -0,0 +1,239 @@ +package com.mall4j.cloud.common.order.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +public class ShopCartItemVO implements Serializable { + + @ApiModelProperty(value = "加入购物车时间", required = true) + private Date createTime; + + @ApiModelProperty(value = "购物车ID", required = true) + private Long cartItemId; + + @ApiModelProperty("店铺ID") + private Long shopId; + + @ApiModelProperty("产品ID") + private Long spuId; + + @ApiModelProperty("SkuID") + private Long skuId; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("购物车产品个数") + private Integer count; + + @ApiModelProperty("是否已经勾选") + private Integer isChecked; + + @ApiModelProperty("售价,加入购物车时的商品价格") + private Long priceFee; + + @ApiModelProperty("当前商品价格") + private Long skuPriceFee; + + @ApiModelProperty("当前总价格(商品价格 * 数量)") + private Long totalPriceFee; + + @ApiModelProperty("当前总价格(商品价格 * 数量)(带小数)") + private Long totalPrice; + + @ApiModelProperty("商品重量") + private BigDecimal weight; + + @ApiModelProperty("商品体积") + private BigDecimal volume; + + @ApiModelProperty("商品图片") + @JsonSerialize(using = ImgJsonSerializer.class) + private String imgUrl; + + @ApiModelProperty(value = "总金额", required = true) + private Long totalAmount; + + @ApiModelProperty("sku规格信息") + private String skuName; + + @ApiModelProperty("spu名称") + private String spuName; + + public Long getCartItemId() { + return cartItemId; + } + + public void setCartItemId(Long cartItemId) { + this.cartItemId = cartItemId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getSkuPriceFee() { + return skuPriceFee; + } + + public void setSkuPriceFee(Long skuPriceFee) { + this.skuPriceFee = skuPriceFee; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Long getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(Long totalAmount) { + this.totalAmount = totalAmount; + } + + public Long getTotalPriceFee() { + return totalPriceFee; + } + + public Integer getIsChecked() { + return isChecked; + } + + public void setIsChecked(Integer isChecked) { + this.isChecked = isChecked; + } + + public void setTotalPriceFee(Long totalPriceFee) { + this.totalPriceFee = totalPriceFee; + } + + public Long getTotalPrice() { + return totalPrice; + } + + public void setTotalPrice(Long totalPrice) { + this.totalPrice = totalPrice; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String toString() { + return "ShopCartItemVO{" + + "createTime=" + createTime + + ", cartItemId=" + cartItemId + + ", shopId=" + shopId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", userId=" + userId + + ", count=" + count + + ", isChecked=" + isChecked + + ", priceFee=" + priceFee + + ", skuPriceFee=" + skuPriceFee + + ", totalPriceFee=" + totalPriceFee + + ", totalPrice=" + totalPrice + + ", weight=" + weight + + ", volume=" + volume + + ", imgUrl='" + imgUrl + '\'' + + ", totalAmount=" + totalAmount + + ", skuName='" + skuName + '\'' + + ", spuName='" + spuName + '\'' + + '}'; + } + + public BigDecimal getWeight() { + return weight; + } + + public void setWeight(BigDecimal weight) { + this.weight = weight; + } + + public BigDecimal getVolume() { + return volume; + } + + public void setVolume(BigDecimal volume) { + this.volume = volume; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderMergerVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderMergerVO.java new file mode 100644 index 00000000..23c0a3f3 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderMergerVO.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.common.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 多个店铺订单合并在一起的合并类 + * "/confirm" 使用 + * @author FrozenWatermelon + */ +public class ShopCartOrderMergerVO { + + @ApiModelProperty(value = "商品总值", required = true) + private Long total; + + @ApiModelProperty(value = "商品总数", required = true) + private Integer totalCount; + + @ApiModelProperty(value = "配送类型 :无需快递") + private Integer dvyType; + + @ApiModelProperty(value = "过滤掉的商品项", required = true) + private List filterShopItems; + + @ApiModelProperty(value = "每个店铺的订单信息", required = true) + List shopCartOrders; + + @ApiModelProperty(value = "用户地址") + private UserAddrVO userAddr; + + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } + + public Integer getDvyType() { + return dvyType; + } + + public void setDvyType(Integer dvyType) { + this.dvyType = dvyType; + } + + public List getShopCartOrders() { + return shopCartOrders; + } + + public void setShopCartOrders(List shopCartOrders) { + this.shopCartOrders = shopCartOrders; + } + + public List getFilterShopItems() { + return filterShopItems; + } + + public void setFilterShopItems(List filterShopItems) { + this.filterShopItems = filterShopItems; + } + + public UserAddrVO getUserAddr() { + return userAddr; + } + + public void setUserAddr(UserAddrVO userAddr) { + this.userAddr = userAddr; + } + + @Override + public String toString() { + return "ShopCartOrderMergerVO{" + + "total=" + total + + ", totalCount=" + totalCount + + ", dvyType=" + dvyType + + ", filterShopItems=" + filterShopItems + + ", shopCartOrders=" + shopCartOrders + + ", userAddr=" + userAddr + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderVO.java new file mode 100644 index 00000000..c0eebee2 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartOrderVO.java @@ -0,0 +1,83 @@ +package com.mall4j.cloud.common.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.List; + +/** + * 单个店铺的订单信息 + * @author FrozenWatermelon + */ +public class ShopCartOrderVO implements Serializable{ + + @ApiModelProperty(value = "店铺id", required = true) + private Long shopId; + + @ApiModelProperty(value = "店铺名称", required = true) + private String shopName; + + @ApiModelProperty(value = "商品总值", required = true) + private Long total; + + @ApiModelProperty(value = "购物车商品", required = true) + private List shopCartItemVO; + + @ApiModelProperty(value = "商品总数", required = true) + private Integer totalCount; + + + + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public List getShopCartItemVO() { + return shopCartItemVO; + } + + public void setShopCartItemVO(List shopCartItemVO) { + this.shopCartItemVO = shopCartItemVO; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } + + + @Override + public String toString() { + return "ShopCartOrderVO{" + + "shopId=" + shopId + + ", shopName='" + shopName + '\'' + + ", total=" + total + + ", shopCartItemVO=" + shopCartItemVO + + ", totalCount=" + totalCount + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartVO.java new file mode 100644 index 00000000..285aaaf1 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartVO.java @@ -0,0 +1,91 @@ +package com.mall4j.cloud.common.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 购物车VO + * + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +public class ShopCartVO { + + @ApiModelProperty(value = "店铺ID", required = true) + private Long shopId; + + @ApiModelProperty(value = "店铺名称", required = true) + private String shopName; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer shopType; + + @ApiModelProperty("购物车商品信息") + private List shopCartItem; + + @ApiModelProperty(value = "商品总值", required = true) + private Long total; + @ApiModelProperty(value = "数量", required = true) + private Integer totalCount; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getShopType() { + return shopType; + } + + public void setShopType(Integer shopType) { + this.shopType = shopType; + } + + public List getshopCartItem() { + return shopCartItem; + } + + public void setshopCartItem(List shopCartItem) { + this.shopCartItem = shopCartItem; + } + + @Override + public String toString() { + return "ShopCartVO{" + + "shopId=" + shopId + + ", shopName='" + shopName + '\'' + + ", shopType=" + shopType + + ", shopCartItem=" + shopCartItem + + ", total=" + total + + ", totalCount=" + totalCount + + '}'; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartWithAmountVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartWithAmountVO.java new file mode 100644 index 00000000..0b7e7a4e --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/ShopCartWithAmountVO.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.common.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/12/14 + */ +public class ShopCartWithAmountVO { + + @ApiModelProperty("总额") + private Long totalMoney; + + @ApiModelProperty("总计") + private Long finalMoney; + + @ApiModelProperty("商品数量") + private Integer count; + + @ApiModelProperty("多个店铺的购物车信息") + private List shopCarts; + + public Long getTotalMoney() { + return totalMoney; + } + + public void setTotalMoney(Long totalMoney) { + this.totalMoney = totalMoney; + } + + public Long getFinalMoney() { + return finalMoney; + } + + public void setFinalMoney(Long finalMoney) { + this.finalMoney = finalMoney; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public List getShopCarts() { + return shopCarts; + } + + public void setShopCarts(List shopCarts) { + this.shopCarts = shopCarts; + } + + @Override + public String toString() { + return "ShopCartWithAmountVO{" + + "totalMoney=" + totalMoney + + ", finalMoney=" + finalMoney + + ", count=" + count + + ", shopCarts=" + shopCarts + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/UserAddrVO.java b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/UserAddrVO.java new file mode 100644 index 00000000..1e118af8 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-order/src/main/java/com/mall4j/cloud/common/order/vo/UserAddrVO.java @@ -0,0 +1,190 @@ +package com.mall4j.cloud.common.order.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 用户地址VO + * + * @author FrozenWatermelon + * @date 2020-12-07 15:50:02 + */ +public class UserAddrVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long addrId; + + @ApiModelProperty("手机") + private String mobile; + + @ApiModelProperty("是否默认地址 1是") + private Integer isDefault; + + @ApiModelProperty("收货人") + private String consignee; + + @ApiModelProperty("省ID") + private Long provinceId; + + @ApiModelProperty("省") + private String province; + + @ApiModelProperty("城市ID") + private Long cityId; + + @ApiModelProperty("城市") + private String city; + + @ApiModelProperty("区ID") + private Long areaId; + + @ApiModelProperty("区") + private String area; + + @ApiModelProperty("邮编") + private String postCode; + + @ApiModelProperty("地址") + private String addr; + + @ApiModelProperty("经度") + private Double lng; + + @ApiModelProperty("纬度") + private Double lat; + + public Long getAddrId() { + return addrId; + } + + public void setAddrId(Long addrId) { + this.addrId = addrId; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getIsDefault() { + return isDefault; + } + + public void setIsDefault(Integer isDefault) { + this.isDefault = isDefault; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public Long getProvinceId() { + return provinceId; + } + + public void setProvinceId(Long provinceId) { + this.provinceId = provinceId; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public Long getCityId() { + return cityId; + } + + public void setCityId(Long cityId) { + this.cityId = cityId; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getPostCode() { + return postCode; + } + + public void setPostCode(String postCode) { + this.postCode = postCode; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public Double getLng() { + return lng; + } + + public void setLng(Double lng) { + this.lng = lng; + } + + public Double getLat() { + return lat; + } + + public void setLat(Double lat) { + this.lat = lat; + } + + @Override + public String toString() { + return "UserAddrVO{" + + "addrId=" + addrId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",mobile=" + mobile + + ",isDefault=" + isDefault + + ",consignee=" + consignee + + ",provinceId=" + provinceId + + ",province=" + province + + ",cityId=" + cityId + + ",city=" + city + + ",areaId=" + areaId + + ",area=" + area + + ",postCode=" + postCode + + ",addr=" + addr + + ",lng=" + lng + + ",lat=" + lat + + '}'; + } +} diff --git a/mall4cloud-common/mall4cloud-common-rocketmq/pom.xml b/mall4cloud-common/mall4cloud-common-rocketmq/pom.xml new file mode 100644 index 00000000..a158a76e --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-rocketmq/pom.xml @@ -0,0 +1,29 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-rocketmq + jar + mall4cloud rocketmq代码 + + + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + + + diff --git a/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqAdapter.java b/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqAdapter.java new file mode 100644 index 00000000..812131bf --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqAdapter.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.common.rocketmq.config; + +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.RocketMQMessageConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * @author FrozenWatermelon + * @date 2021/3/30 + */ +@RefreshScope +@Configuration +public class RocketMqAdapter { + + @Autowired + private RocketMQMessageConverter rocketMqMessageConverter; + + @Value("${rocketmq.name-server:}") + private String nameServer; + + public RocketMQTemplate getTemplateByTopicName(String topic){ + RocketMQTemplate mqTemplate = new RocketMQTemplate(); + DefaultMQProducer producer = new DefaultMQProducer(topic); + producer.setNamesrvAddr(nameServer); + producer.setRetryTimesWhenSendFailed(2); + producer.setSendMsgTimeout((int) RocketMqConstant.TIMEOUT); + mqTemplate.setProducer(producer); + mqTemplate.setMessageConverter(rocketMqMessageConverter.getMessageConverter()); + return mqTemplate; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqConstant.java b/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqConstant.java new file mode 100644 index 00000000..e913df92 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-rocketmq/src/main/java/com/mall4j/cloud/common/rocketmq/config/RocketMqConstant.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.common.rocketmq.config; + +/** + * nameserver用;分割 + * 同步消息,如果两次 + * @author FrozenWatermelon + * @date 2021/3/25 + */ +public class RocketMqConstant { + + // 延迟消息 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h (1-18) + + /** + * 取消订单时间,实际上30分钟 + */ + public static final int CANCEL_ORDER_DELAY_LEVEL = 16; + + /** + * 默认发送消息超时时间 + */ + public static final long TIMEOUT = 3000; + + + /** + * 库存解锁topic + */ + public static final String STOCK_UNLOCK_TOPIC = "stock-unlock-topic"; + + /** + * 订单取消 + */ + public static final String ORDER_CANCEL_TOPIC = "order-cancel-topic"; + + /** + * 订单支付成功 + */ + public static final String ORDER_NOTIFY_TOPIC = "order-notify-topic"; + + /** + * 订单支付成功 通知库存服务 + */ + public static final String ORDER_NOTIFY_STOCK_TOPIC = "order-notify-stock-topic"; + /** + * 商品搜索topic + */ + public static final String CANAL_TOPIC = "canal-topic"; + + + +} diff --git a/mall4cloud-common/mall4cloud-common-security/pom.xml b/mall4cloud-common/mall4cloud-common-security/pom.xml new file mode 100644 index 00000000..a3f3935e --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/pom.xml @@ -0,0 +1,49 @@ + + + + mall4cloud-common + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common-security + jar + mall4cloud 安全相关代码 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + com.mall4j.cloud + mall4cloud-api-auth + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-rbac + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-cache + ${project.version} + + + org.springframework.security + spring-security-crypto + + + + + diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/AuthUserContext.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/AuthUserContext.java new file mode 100644 index 00000000..9f80b01d --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/AuthUserContext.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.common.security; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; + +/** + * @author FrozenWatermelon + * @date 2020/7/16 + */ +public class AuthUserContext { + + /** The request holder. */ + private static final ThreadLocal USER_INFO_IN_TOKEN_HOLDER = new ThreadLocal<>(); + + public static UserInfoInTokenBO get() { + return USER_INFO_IN_TOKEN_HOLDER.get(); + } + + + public static UserInfoInTokenBO forceGet() { + return USER_INFO_IN_TOKEN_HOLDER.get(); + } + + public static void set(UserInfoInTokenBO userInfoInTokenBo) { + USER_INFO_IN_TOKEN_HOLDER.set(userInfoInTokenBo); + } + + public static void clean() { + if (USER_INFO_IN_TOKEN_HOLDER.get() != null) { + USER_INFO_IN_TOKEN_HOLDER.remove(); + } + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/AuthConfigAdapter.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/AuthConfigAdapter.java new file mode 100644 index 00000000..7e629cb7 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/AuthConfigAdapter.java @@ -0,0 +1,26 @@ +package com.mall4j.cloud.common.security.adapter; + +import java.util.List; + +/** + * 实现该接口之后,修改需要授权登陆的路径,不需要授权登陆的路径 + * + * @author FrozenWatermelon + * @date 2020/7/4 + */ +public interface AuthConfigAdapter { + + /** + * 需要授权登陆的路径 + * @return 需要授权登陆的路径列表 + */ + List pathPatterns(); + + /** + * 不需要授权登陆的路径 + * @param paths + * @return 不需要授权登陆的路径列表 + */ + List excludePathPatterns(String... paths); + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/DefaultAuthConfigAdapter.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/DefaultAuthConfigAdapter.java new file mode 100644 index 00000000..e196f517 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/adapter/DefaultAuthConfigAdapter.java @@ -0,0 +1,53 @@ +package com.mall4j.cloud.common.security.adapter; + +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/7/16 + */ +public class DefaultAuthConfigAdapter implements AuthConfigAdapter { + + private static final Logger logger = LoggerFactory.getLogger(DefaultAuthConfigAdapter.class); + + public DefaultAuthConfigAdapter() { + logger.info("not implement other AuthConfigAdapter, use DefaultAuthConfigAdapter... all url need auth..."); + } + + /** + * 内部直接调用接口,无需登录权限 + */ + private static final String FEIGN_INSIDER_URI = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/**"; + + /** + * 外部直接调用接口,无需登录权限 unwanted auth + */ + private static final String EXTERNAL_URI = "/**/ua/**"; + + /** + * swagger + */ + private static final String DOC_URI = "/v2/api-docs"; + + @Override + public List pathPatterns() { + return Collections.singletonList("/*"); + } + + @Override + public List excludePathPatterns(String... paths) { + List arrayList = new ArrayList<>(); + arrayList.add(DOC_URI); + arrayList.add(FEIGN_INSIDER_URI); + arrayList.add(EXTERNAL_URI); + arrayList.addAll(Arrays.asList(paths)); + return arrayList; + } +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/AuthAccountInVerifyBO.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/AuthAccountInVerifyBO.java new file mode 100644 index 00000000..b4a8989d --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/AuthAccountInVerifyBO.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.common.security.bo; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; + +/** + * 用于校验的用户信息 + * + * @author FrozenWatermelon + * @date 2020/7/3 + */ +public class AuthAccountInVerifyBO extends UserInfoInTokenBO { + + private String password; + + private Integer status; + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "AuthAccountInVerifyBO{" + + "password='" + password + '\'' + + ", status=" + status + + "} " + super.toString(); + } + + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/TokenInfoBO.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/TokenInfoBO.java new file mode 100644 index 00000000..82d86add --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/bo/TokenInfoBO.java @@ -0,0 +1,65 @@ +package com.mall4j.cloud.common.security.bo; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; + +/** + * token信息,该信息存在redis中 + * + * @author FrozenWatermelon + * @date 2020/7/2 + */ +public class TokenInfoBO { + + /** + * 保存在token信息里面的用户信息 + */ + private UserInfoInTokenBO userInfoInToken; + + private String accessToken; + + private String refreshToken; + + /** + * 在多少秒后过期 + */ + private Integer expiresIn; + + public UserInfoInTokenBO getUserInfoInToken() { + return userInfoInToken; + } + + public void setUserInfoInToken(UserInfoInTokenBO userInfoInToken) { + this.userInfoInToken = userInfoInToken; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public Integer getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + } + + @Override + public String toString() { + return "TokenInfoBO{" + "userInfoInToken=" + userInfoInToken + ", accessToken='" + accessToken + '\'' + + ", refreshToken='" + refreshToken + '\'' + ", expiresIn=" + expiresIn + '}'; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/AuthConfig.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/AuthConfig.java new file mode 100644 index 00000000..631f7e1e --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/AuthConfig.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.common.security.config; + +import cn.hutool.core.util.ArrayUtil; +import com.mall4j.cloud.common.security.adapter.AuthConfigAdapter; +import com.mall4j.cloud.common.security.adapter.DefaultAuthConfigAdapter; +import com.mall4j.cloud.common.security.filter.AuthFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +import javax.servlet.DispatcherType; + +/** + * 授权配置 + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@Configuration +public class AuthConfig { + + @Autowired + private AuthFilter authFilter; + + @Bean + @ConditionalOnMissingBean + public AuthConfigAdapter authConfigAdapter() { + return new DefaultAuthConfigAdapter(); + } + + @Bean + @Lazy + public FilterRegistrationBean filterRegistration(AuthConfigAdapter authConfigAdapter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + // 添加过滤器 + registration.setFilter(authFilter); + // 设置过滤路径,/*所有路径 + registration.addUrlPatterns(ArrayUtil.toArray(authConfigAdapter.pathPatterns(), String.class)); + registration.setName("authFilter"); + // 设置优先级 + registration.setOrder(0); + registration.setDispatcherTypes(DispatcherType.REQUEST); + return registration; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/PasswordConfig.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/PasswordConfig.java new file mode 100644 index 00000000..20c361cf --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/config/PasswordConfig.java @@ -0,0 +1,20 @@ +package com.mall4j.cloud.common.security.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * @author FrozenWatermelon + * @date 2020/7/3 + */ +@Configuration +public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/constant/InputUserNameEnum.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/constant/InputUserNameEnum.java new file mode 100644 index 00000000..7a4c8fab --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/constant/InputUserNameEnum.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.common.security.constant; + +/** + * 输入的用户名类型枚举 1.username 2.mobile 3.email + * @author FrozenWatermelon + * @date 2020/7/3 + */ +public enum InputUserNameEnum { + + /** + * 用户名 + */ + USERNAME(1), + + /** + * 手机号 + */ + PHONE(2), + + /** + * 邮箱 + */ + EMAIL(3),; + + private final Integer value; + + public Integer value() { + return value; + } + + InputUserNameEnum(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/AuthFilter.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/AuthFilter.java new file mode 100644 index 00000000..646d3d13 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/AuthFilter.java @@ -0,0 +1,167 @@ +package com.mall4j.cloud.common.security.filter; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.feign.TokenFeignClient; +import com.mall4j.cloud.api.rbac.constant.HttpMethodEnum; +import com.mall4j.cloud.api.rbac.feign.PermissionFeignClient; +import com.mall4j.cloud.common.constant.Auth; +import com.mall4j.cloud.common.feign.FeignInsideAuthConfig; +import com.mall4j.cloud.common.handler.HttpHandler; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.security.adapter.AuthConfigAdapter; +import com.mall4j.cloud.common.util.IpHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +/** + * 授权过滤,只要实现AuthConfigAdapter接口,添加对应路径即可: + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@Component +public class AuthFilter implements Filter { + + private static Logger logger = LoggerFactory.getLogger(AuthFilter.class); + + @Autowired + private AuthConfigAdapter authConfigAdapter; + + @Autowired + private HttpHandler httpHandler; + + @Autowired + private TokenFeignClient tokenFeignClient; + + @Autowired + private PermissionFeignClient permissionFeignClient; + + @Autowired + private FeignInsideAuthConfig feignInsideAuthConfig; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + + if (!feignRequestCheck(req)) { + httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED)); + return; + } + + if (Auth.CHECK_TOKEN_URI.equals(req.getRequestURI())) { + chain.doFilter(req, resp); + return; + } + + + List excludePathPatterns = authConfigAdapter.excludePathPatterns(); + + // 如果匹配不需要授权的路径,就不需要校验是否需要授权 + if (CollectionUtil.isNotEmpty(excludePathPatterns)) { + for (String excludePathPattern : excludePathPatterns) { + AntPathMatcher pathMatcher = new AntPathMatcher(); + if (pathMatcher.match(excludePathPattern, req.getRequestURI())) { + chain.doFilter(req, resp); + return; + } + } + } + + String accessToken = req.getHeader("Authorization"); + + if (StrUtil.isBlank(accessToken)) { + httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED)); + return; + } + + // 校验token,并返回用户信息 + ServerResponseEntity userInfoInTokenVoServerResponseEntity = tokenFeignClient + .checkToken(accessToken); + if (!userInfoInTokenVoServerResponseEntity.isSuccess()) { + httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED)); + return; + } + + UserInfoInTokenBO userInfoInToken = userInfoInTokenVoServerResponseEntity.getData(); + + // 需要用户角色权限,就去根据用户角色权限判断是否 + if (!checkRbac(userInfoInToken,req.getRequestURI(), req.getMethod())) { + httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED)); + return; + } + + try { + // 保存上下文 + AuthUserContext.set(userInfoInToken); + + chain.doFilter(req, resp); + } + finally { + AuthUserContext.clean(); + } + + } + + private boolean feignRequestCheck(HttpServletRequest req) { + // 不是feign请求,不用校验 + if (!req.getRequestURI().startsWith(FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX)) { + return true; + } + String feignInsideSecret = req.getHeader(feignInsideAuthConfig.getKey()); + + // 校验feign 请求携带的key 和 value是否正确 + if (StrUtil.isBlank(feignInsideSecret) || !Objects.equals(feignInsideSecret,feignInsideAuthConfig.getSecret())) { + return false; + } + // ip白名单 + List ips = feignInsideAuthConfig.getIps(); + // 移除无用的空ip + ips.removeIf(StrUtil::isBlank); + // 有ip白名单,且ip不在白名单内,校验失败 + if (CollectionUtil.isNotEmpty(ips) + && !ips.contains(IpHelper.getIpAddr())) { + logger.error("ip not in ip White list: {}, ip, {}", ips, IpHelper.getIpAddr()); + return false; + } + return true; + } + + /** + * 用户角色权限校验 + * @param uri uri + * @return 是否校验成功 + */ + public boolean checkRbac(UserInfoInTokenBO userInfoInToken, String uri, String method) { + + if (!Objects.equals(SysTypeEnum.PLATFORM.value(), userInfoInToken.getSysType()) && !Objects.equals(SysTypeEnum.MULTISHOP.value(), userInfoInToken.getSysType())) { + return true; + } + + ServerResponseEntity booleanServerResponseEntity = permissionFeignClient + .checkPermission(userInfoInToken.getUserId(), userInfoInToken.getSysType(),uri,userInfoInToken.getIsAdmin(),HttpMethodEnum.valueOf(method.toUpperCase()).value() ); + + if (!booleanServerResponseEntity.isSuccess()) { + return false; + } + + return booleanServerResponseEntity.getData(); + } + +} diff --git a/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/XssFilter.java b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/XssFilter.java new file mode 100644 index 00000000..4652f788 --- /dev/null +++ b/mall4cloud-common/mall4cloud-common-security/src/main/java/com/mall4j/cloud/common/security/filter/XssFilter.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.common.security.filter; + +import com.mall4j.cloud.common.xss.XssWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 一些简单的安全过滤: xss + * + * @author FrozenWatermelon + * @date 2020/7/11 + */ +@WebFilter(filterName = "xssFilter") +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class XssFilter implements Filter { + + private static final Logger logger = LoggerFactory.getLogger(XssFilter.class); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + + // replaceAll("[\r\n]" =》 Potential CRLF Injection for logs + logger.info("AuthFilter RequestURI :{}", req.getRequestURI().replaceAll("[\r\n]","")); + // xss 过滤 + chain.doFilter(new XssWrapper(req), resp); + } +} diff --git a/mall4cloud-common/pom.xml b/mall4cloud-common/pom.xml new file mode 100644 index 00000000..758ef6cb --- /dev/null +++ b/mall4cloud-common/pom.xml @@ -0,0 +1,25 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-common + pom + + mall4cloud 公共模块 + + mall4cloud-common-core + mall4cloud-common-database + mall4cloud-common-cache + mall4cloud-common-security + mall4cloud-common-order + mall4cloud-common-rocketmq + + + diff --git a/mall4cloud-gateway/pom.xml b/mall4cloud-gateway/pom.xml new file mode 100644 index 00000000..22760296 --- /dev/null +++ b/mall4cloud-gateway/pom.xml @@ -0,0 +1,52 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-gateway + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/GatewayApplication.java b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/GatewayApplication.java new file mode 100644 index 00000000..c611828c --- /dev/null +++ b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/GatewayApplication.java @@ -0,0 +1,17 @@ +package com.mall4j.cloud.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author FrozenWatermelon + * @date 2020/7/23 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +public class GatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } + +} diff --git a/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/config/SwaggerResourceConfig.java b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/config/SwaggerResourceConfig.java new file mode 100644 index 00000000..13e120c2 --- /dev/null +++ b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/config/SwaggerResourceConfig.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.gateway.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.gateway.config.GatewayProperties; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.support.NameUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import springfox.documentation.swagger.web.SwaggerResource; +import springfox.documentation.swagger.web.SwaggerResourcesProvider; + +import java.util.ArrayList; +import java.util.List; + +/*** + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +@Component +@Primary +public class SwaggerResourceConfig implements SwaggerResourcesProvider { + + private static final Logger log = LoggerFactory.getLogger(SwaggerResourceConfig.class); + + private final RouteLocator routeLocator; + + private final GatewayProperties gatewayProperties; + + public SwaggerResourceConfig(RouteLocator routeLocator, GatewayProperties gatewayProperties) { + this.routeLocator = routeLocator; + this.gatewayProperties = gatewayProperties; + } + + @Override + public List get() { + List resources = new ArrayList<>(); + List routes = new ArrayList<>(); + routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); + for (RouteDefinition routeDefinition : gatewayProperties.getRoutes()) { + if (routes.contains(routeDefinition.getId())) { + for (PredicateDefinition predicateDefinition : routeDefinition.getPredicates()) { + if (("Path").equalsIgnoreCase(predicateDefinition.getName())) { + resources + .add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs() + .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("**", "v2/api-docs"))); + } + } + } + } + + return resources; + } + + private SwaggerResource swaggerResource(String name, String location) { + log.info("name:{},location:{}", name, location); + SwaggerResource swaggerResource = new SwaggerResource(); + swaggerResource.setName(name); + swaggerResource.setLocation(location); + swaggerResource.setSwaggerVersion("2.0"); + return swaggerResource; + } + +} diff --git a/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/handler/SwaggerHandler.java b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/handler/SwaggerHandler.java new file mode 100644 index 00000000..e515894c --- /dev/null +++ b/mall4cloud-gateway/src/main/java/com/mall4j/cloud/gateway/handler/SwaggerHandler.java @@ -0,0 +1,52 @@ +package com.mall4j.cloud.gateway.handler; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import springfox.documentation.swagger.web.*; + +import java.util.Optional; + +/** + * swagger-resource + * @author fsl + * @date 2019-06-0310:47 + */ +@RestController +public class SwaggerHandler { + + @Autowired(required = false) + private SecurityConfiguration securityConfiguration; + + @Autowired(required = false) + private UiConfiguration uiConfiguration; + + private final SwaggerResourcesProvider swaggerResources; + + @Autowired + public SwaggerHandler(SwaggerResourcesProvider swaggerResources) { + this.swaggerResources = swaggerResources; + } + + + @GetMapping("/swagger-resources/configuration/security") + public Mono> securityConfiguration() { + return Mono.just(new ResponseEntity<>( + Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); + } + + @GetMapping("/swagger-resources/configuration/ui") + public Mono> uiConfiguration() { + return Mono.just(new ResponseEntity<>( + Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); + } + + @GetMapping("/swagger-resources") + public Mono swaggerResources() { + return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); + } + +} diff --git a/mall4cloud-gateway/src/main/resources/bootstrap.yml b/mall4cloud-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..64c056d0 --- /dev/null +++ b/mall4cloud-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 8000 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-leaf/pom.xml b/mall4cloud-leaf/pom.xml new file mode 100644 index 00000000..b15a4a37 --- /dev/null +++ b/mall4cloud-leaf/pom.xml @@ -0,0 +1,53 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-leaf + jar + + mall4cloud 基于美团leaf的生成id服务 + + + mysql + mysql-connector-java + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-api-leaf + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/IDGen.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/IDGen.java new file mode 100644 index 00000000..ca716e7d --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/IDGen.java @@ -0,0 +1,23 @@ +package com.mall4j.cloud.leaf; + +import com.mall4j.cloud.leaf.common.Result; + +/** + * @author leaf + */ +public interface IDGen { + + /** + * get + * @param key key + * @return Result + */ + Result get(String key); + + /** + * init + * @return inited + */ + boolean init(); + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/LeafServerApplication.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/LeafServerApplication.java new file mode 100644 index 00000000..5d2b810c --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/LeafServerApplication.java @@ -0,0 +1,16 @@ +package com.mall4j.cloud.leaf; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author leaf + */ +@SpringBootApplication +public class LeafServerApplication { + + public static void main(String[] args) { + SpringApplication.run(LeafServerApplication.class, args); + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Result.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Result.java new file mode 100644 index 00000000..6c976e51 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Result.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.leaf.common; + +/** + * @author leaf + */ +public class Result { + + private long id; + + private Status status; + + public Result() { + + } + + public Result(long id, Status status) { + this.id = id; + this.status = status; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + @Override + public String toString() { + return "Result{" + "id=" + id + + ", status=" + status + + '}'; + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Status.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Status.java new file mode 100644 index 00000000..ec87772a --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/Status.java @@ -0,0 +1,18 @@ +package com.mall4j.cloud.leaf.common; + +/** + * @author leaf + */ +public enum Status { + + /** + * success + */ + SUCCESS, + + /** + * exception + */ + EXCEPTION + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/ZeroIDGen.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/ZeroIDGen.java new file mode 100644 index 00000000..3f093f03 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/common/ZeroIDGen.java @@ -0,0 +1,20 @@ +package com.mall4j.cloud.leaf.common; + +import com.mall4j.cloud.leaf.IDGen; + +/** + * @author left + */ +public class ZeroIDGen implements IDGen { + + @Override + public Result get(String key) { + return new Result(0, Status.SUCCESS); + } + + @Override + public boolean init() { + return true; + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/InitException.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/InitException.java new file mode 100644 index 00000000..b4c964d2 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/InitException.java @@ -0,0 +1,12 @@ +package com.mall4j.cloud.leaf.exception; + +/** + * @author leaf + */ +public class InitException extends Exception { + + public InitException(String msg) { + super(msg); + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/LeafServerException.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/LeafServerException.java new file mode 100644 index 00000000..617d82bb --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/LeafServerException.java @@ -0,0 +1,16 @@ +package com.mall4j.cloud.leaf.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * @author leaf + */ +@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) +public class LeafServerException extends RuntimeException { + + public LeafServerException(String msg) { + super(msg); + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/NoKeyException.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/NoKeyException.java new file mode 100644 index 00000000..09b1b258 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/exception/NoKeyException.java @@ -0,0 +1,12 @@ +package com.mall4j.cloud.leaf.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * @author leaf + */ +@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Key is none") +public class NoKeyException extends RuntimeException { + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/feign/SegmentFeignController.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/feign/SegmentFeignController.java new file mode 100644 index 00000000..29e14fb8 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/feign/SegmentFeignController.java @@ -0,0 +1,48 @@ +package com.mall4j.cloud.leaf.feign; + +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.leaf.common.Result; +import com.mall4j.cloud.leaf.common.Status; +import com.mall4j.cloud.leaf.exception.LeafServerException; +import com.mall4j.cloud.leaf.exception.NoKeyException; +import com.mall4j.cloud.leaf.service.SegmentService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Objects; + + +/** + * @author FrozenWatermelon + * @date 2020/7/15 + */ +@RestController +public class SegmentFeignController implements SegmentFeignClient { + + private static final Logger logger = LoggerFactory.getLogger(SegmentFeignController.class); + + + @Autowired + private SegmentService segmentService; + + @Override + public ServerResponseEntity getSegmentId(String key) { + return ServerResponseEntity.success(get(key, segmentService.getId(key))); + } + + + private Long get(String key, Result id) { + Result result; + if (key == null || key.isEmpty()) { + throw new NoKeyException(); + } + result = id; + if (Objects.equals(result.getStatus(), Status.EXCEPTION)) { + throw new LeafServerException(result.toString()); + } + return result.getId(); + } +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/SegmentIDGenImpl.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/SegmentIDGenImpl.java new file mode 100644 index 00000000..d6030cc3 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/SegmentIDGenImpl.java @@ -0,0 +1,336 @@ +package com.mall4j.cloud.leaf.segment; + +import com.mall4j.cloud.leaf.IDGen; +import com.mall4j.cloud.leaf.common.Result; +import com.mall4j.cloud.leaf.common.Status; +import com.mall4j.cloud.leaf.segment.dao.IDAllocDao; +import com.mall4j.cloud.leaf.segment.model.LeafAlloc; +import com.mall4j.cloud.leaf.segment.model.Segment; +import com.mall4j.cloud.leaf.segment.model.SegmentBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.SecureRandom; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author leaf + */ +public class SegmentIDGenImpl implements IDGen { + + private static final Logger logger = LoggerFactory.getLogger(SegmentIDGenImpl.class); + + /** + * IDCache未初始化成功时的异常码 + */ + private static final long EXCEPTION_ID_IDCACHE_INIT_FALSE = -1; + + /** + * key不存在时的异常码 + */ + private static final long EXCEPTION_ID_KEY_NOT_EXISTS = -2; + + /** + * SegmentBuffer中的两个Segment均未从DB中装载时的异常码 + */ + private static final long EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL = -3; + + /** + * 最大步长不超过100,0000 + */ + private static final int MAX_STEP = 1000000; + + /** + * 一个Segment维持时间为15分钟 + */ + private static final long SEGMENT_DURATION = 15 * 60 * 1000L; + + private final ExecutorService service = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, + new SynchronousQueue<>(), new UpdateThreadFactory()); + + private volatile boolean initOk = false; + + private final Map cache = new ConcurrentHashMap<>(); + + private IDAllocDao dao; + + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final int DEFAULT_LOAD_FACTOR = 2; + + public static class UpdateThreadFactory implements ThreadFactory { + + private static int threadInitNumber = 0; + + private static synchronized int nextThreadNum() { + return threadInitNumber++; + } + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "Thread-Segment-Update-" + nextThreadNum()); + } + + } + + @Override + public boolean init() { + logger.info("Init ..."); + // 确保加载到kv后才初始化成功 + updateCacheFromDb(); + initOk = true; + updateCacheFromDbAtEveryMinute(); + return initOk; + } + + private void updateCacheFromDbAtEveryMinute() { + ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("check-idCache-thread"); + t.setDaemon(true); + return t; + } + }); + service.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + updateCacheFromDb(); + } + }, 60, 60, TimeUnit.SECONDS); + } + + private void updateCacheFromDb() { + logger.info("update cache from db"); + try { + List dbTags = dao.getAllTags(); + if (dbTags == null || dbTags.isEmpty()) { + return; + } + List cacheTags = new ArrayList(cache.keySet()); + Set insertTagsSet = new HashSet<>(dbTags); + Set removeTagsSet = new HashSet<>(cacheTags); + // db中新加的tags灌进cache + for (int i = 0; i < cacheTags.size(); i++) { + String tmp = cacheTags.get(i); + insertTagsSet.remove(tmp); + } + for (String tag : insertTagsSet) { + SegmentBuffer buffer = new SegmentBuffer(); + buffer.setKey(tag); + Segment segment = buffer.getCurrent(); + segment.setValue(new AtomicLong(0)); + segment.setMax(0); + segment.setStep(0); + cache.put(tag, buffer); + logger.info("Add tag {} from db to IdCache, SegmentBuffer {}", tag, buffer); + } + // cache中已失效的tags从cache删除 + for (int i = 0; i < dbTags.size(); i++) { + String tmp = dbTags.get(i); + removeTagsSet.remove(tmp); + } + for (String tag : removeTagsSet) { + cache.remove(tag); + logger.info("Remove tag {} from IdCache", tag); + } + } + catch (Exception e) { + logger.warn("update cache from db exception", e); + } + } + + @Override + public Result get(final String key) { + if (!initOk) { + return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION); + } + SegmentBuffer buffer = cache.get(key); + if (buffer != null) { + if (buffer.isInitOk()) { + synchronized (buffer) { + if (buffer.isInitOk()) { + try { + updateSegmentFromDb(key, buffer.getCurrent()); + logger.info("Init buffer. Update leafkey {} {} from db", key, buffer.getCurrent()); + buffer.setInitOk(true); + } + catch (Exception e) { + logger.warn("Init buffer {} exception", buffer.getCurrent(), e); + } + } + } + } + return getIdFromSegmentBuffer(cache.get(key)); + } + return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION); + } + + public void updateSegmentFromDb(String key, Segment segment) { + SegmentBuffer buffer = segment.getBuffer(); + LeafAlloc leafAlloc; + if (buffer.isInitOk()) { + leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key); + buffer.setStep(leafAlloc.getStep()); + // leafAlloc中的step为DB中的step + buffer.setMinStep(leafAlloc.getStep()); + } + else if (buffer.getUpdateTimestamp() == 0) { + leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key); + buffer.setUpdateTimestamp(System.currentTimeMillis()); + buffer.setStep(leafAlloc.getStep()); + // leafAlloc中的step为DB中的step + buffer.setMinStep(leafAlloc.getStep()); + } + else { + long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp(); + int nextStep = buffer.getStep(); + if (duration < SEGMENT_DURATION) { + if (nextStep * DEFAULT_LOAD_FACTOR > MAX_STEP) { + // do nothing + } + else { + nextStep = nextStep * DEFAULT_LOAD_FACTOR; + } + } + else if (duration < SEGMENT_DURATION * DEFAULT_LOAD_FACTOR) { + // do nothing with nextStep + } + else { + nextStep = nextStep / DEFAULT_LOAD_FACTOR >= buffer.getMinStep() ? nextStep / DEFAULT_LOAD_FACTOR + : nextStep; + } + logger.info("leafKey[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(), + String.format("%.2f", ((double) duration / (1000 * 60))), nextStep); + LeafAlloc temp = new LeafAlloc(); + temp.setKey(key); + temp.setStep(nextStep); + leafAlloc = dao.updateMaxIdByCustomStepAndGetLeafAlloc(temp); + buffer.setUpdateTimestamp(System.currentTimeMillis()); + buffer.setStep(nextStep); + // leafAlloc的step为DB中的step + buffer.setMinStep(leafAlloc.getStep()); + } + // must set value before set max + long value = leafAlloc.getMaxId() - buffer.getStep(); + segment.getValue().set(value); + segment.setMax(leafAlloc.getMaxId()); + segment.setStep(buffer.getStep()); + segment.setRandomStep(leafAlloc.getRandomStep()); + } + + public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) { + while (true) { + buffer.rLock().lock(); + try { + final Segment segment = buffer.getCurrent(); + if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep()) + && buffer.getThreadRunning().compareAndSet(false, true)) { + service.execute(new Runnable() { + @Override + public void run() { + Segment next = buffer.getSegments()[buffer.nextPos()]; + boolean updateOk = false; + try { + updateSegmentFromDb(buffer.getKey(), next); + updateOk = true; + logger.info("update segment {} from db {}", buffer.getKey(), next); + } + catch (Exception e) { + logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e); + } + finally { + if (updateOk) { + buffer.wLock().lock(); + buffer.setNextReady(true); + buffer.getThreadRunning().set(false); + buffer.wLock().unlock(); + } + else { + buffer.getThreadRunning().set(false); + } + } + } + }); + } + long value; + if (segment.getRandomStep() > 1) { + + // 随机从1-10里面增加 + value = segment.getValue().getAndAdd(randomAdd(segment.getRandomStep())); + } + else { + value = segment.getValue().getAndIncrement(); + } + + if (value < segment.getMax()) { + return new Result(value, Status.SUCCESS); + } + } + finally { + buffer.rLock().unlock(); + } + waitAndSleep(buffer); + buffer.wLock().lock(); + try { + final Segment segment = buffer.getCurrent(); + long value = segment.getValue().getAndIncrement(); + if (value < segment.getMax()) { + return new Result(value, Status.SUCCESS); + } + if (buffer.isNextReady()) { + buffer.switchPos(); + buffer.setNextReady(false); + } + else { + logger.error("Both two segments in {} are not ready!", buffer); + return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION); + } + } + finally { + buffer.wLock().unlock(); + } + } + } + + private int randomAdd(int randomStep) { + return RANDOM.nextInt(randomStep - 1) + 1; + } + + private void waitAndSleep(SegmentBuffer buffer) { + int roll = 0; + while (buffer.getThreadRunning().get()) { + roll += 1; + if (roll > 10000) { + try { + TimeUnit.MILLISECONDS.sleep(10); + break; + } + catch (InterruptedException e) { + logger.warn("Thread {} Interrupted, Exception: {}", Thread.currentThread().getName(), e); + break; + } + } + } + } + + public List getAllLeafAllocs() { + return dao.getAllLeafAllocs(); + } + + public Map getCache() { + return cache; + } + + public IDAllocDao getDao() { + return dao; + } + + public void setDao(IDAllocDao dao) { + this.dao = dao; + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocDao.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocDao.java new file mode 100644 index 00000000..0d77092a --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocDao.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.leaf.segment.dao; + +import com.mall4j.cloud.leaf.segment.model.LeafAlloc; + +import java.util.List; + +/** + * @author leaf + */ +public interface IDAllocDao { + + /** + * com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.getAllLeafAllocs + * @return List + */ + List getAllLeafAllocs(); + + /** + * updateMaxIdAndGetLeafAlloc + * @param tag tag + * @return LeafAlloc + */ + LeafAlloc updateMaxIdAndGetLeafAlloc(String tag); + + /** + * updateMaxIdByCustomStepAndGetLeafAlloc + * @param leafAlloc leafAlloc + * @return LeafAlloc + */ + LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc); + + /** + * getAllTags + * @return List + */ + List getAllTags(); + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocMapper.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocMapper.java new file mode 100644 index 00000000..1bea8ecc --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/IDAllocMapper.java @@ -0,0 +1,53 @@ +package com.mall4j.cloud.leaf.segment.dao; + +import com.mall4j.cloud.leaf.segment.model.LeafAlloc; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +/** + * @author leaf + */ +public interface IDAllocMapper { + + /** + * getAllLeafAllocs + * @return List + */ + @Select("SELECT biz_tag, max_id, step, update_time FROM leaf_alloc") + @Results(value = { @Result(column = "biz_tag", property = "key"), @Result(column = "max_id", property = "maxId"), + @Result(column = "step", property = "step"), @Result(column = "update_time", property = "updateTime") }) + List getAllLeafAllocs(); + + /** + * getLeafAlloc + * @param tag tag + * @return LeafAlloc + */ + @Select("SELECT biz_tag, max_id, step, random_step FROM leaf_alloc WHERE biz_tag = #{tag}") + @Results(value = { @Result(column = "biz_tag", property = "key"), @Result(column = "max_id", property = "maxId"), + @Result(column = "step", property = "step"), @Result(column = "random_step", property = "randomStep") }) + LeafAlloc getLeafAlloc(@Param("tag") String tag); + + /** + * updateMaxId + * @param tag tag + */ + @Update("UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = #{tag}") + void updateMaxId(@Param("tag") String tag); + + /** + * updateMaxIdByCustomStep + * @param leafAlloc leafAlloc + */ + @Update("UPDATE leaf_alloc SET max_id = max_id + #{step} WHERE biz_tag = #{key}") + void updateMaxIdByCustomStep(@Param("leafAlloc") LeafAlloc leafAlloc); + + /** + * getAllTags + * @return List + */ + @Select("SELECT biz_tag FROM leaf_alloc") + List getAllTags(); + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/impl/IDAllocDaoImpl.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/impl/IDAllocDaoImpl.java new file mode 100644 index 00000000..e7189c16 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/dao/impl/IDAllocDaoImpl.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.leaf.segment.dao.impl; + +import com.mall4j.cloud.leaf.segment.dao.IDAllocDao; +import com.mall4j.cloud.leaf.segment.dao.IDAllocMapper; +import com.mall4j.cloud.leaf.segment.model.LeafAlloc; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; + +import javax.sql.DataSource; +import java.util.List; + +/** + * @author leaf + */ +public class IDAllocDaoImpl implements IDAllocDao { + + final SqlSessionFactory sqlSessionFactory; + + public IDAllocDaoImpl(DataSource dataSource) { + TransactionFactory transactionFactory = new JdbcTransactionFactory(); + Environment environment = new Environment("development", transactionFactory, dataSource); + Configuration configuration = new Configuration(environment); + configuration.addMapper(IDAllocMapper.class); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); + } + + @Override + public List getAllLeafAllocs() { + try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { + return sqlSession.selectList("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.getAllLeafAllocs"); + } + } + + @Override + public LeafAlloc updateMaxIdAndGetLeafAlloc(String tag) { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + sqlSession.update("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.updateMaxId", tag); + LeafAlloc result = sqlSession.selectOne("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.getLeafAlloc", tag); + sqlSession.commit(); + return result; + } + } + + @Override + public LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc) { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + sqlSession.update("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.updateMaxIdByCustomStep", leafAlloc); + LeafAlloc result = sqlSession.selectOne("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.getLeafAlloc", + leafAlloc.getKey()); + sqlSession.commit(); + return result; + } + } + + @Override + public List getAllTags() { + try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { + return sqlSession.selectList("com.mall4j.cloud.leaf.segment.dao.IDAllocMapper.getAllTags"); + } + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/LeafAlloc.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/LeafAlloc.java new file mode 100644 index 00000000..72cb7548 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/LeafAlloc.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.leaf.segment.model; + +/** + * @author leaf + */ +public class LeafAlloc { + + private String key; + + private long maxId; + + private int step; + + private String updateTime; + + private int randomStep; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public long getMaxId() { + return maxId; + } + + public void setMaxId(long maxId) { + this.maxId = maxId; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getRandomStep() { + return randomStep; + } + + public void setRandomStep(int randomStep) { + this.randomStep = randomStep; + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/Segment.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/Segment.java new file mode 100644 index 00000000..19576755 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/Segment.java @@ -0,0 +1,75 @@ +package com.mall4j.cloud.leaf.segment.model; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author left + */ +public class Segment { + + private AtomicLong value = new AtomicLong(0); + + private volatile long max; + + private volatile int step; + + private volatile int randomStep; + + private final SegmentBuffer buffer; + + public Segment(SegmentBuffer buffer) { + this.buffer = buffer; + } + + public AtomicLong getValue() { + return value; + } + + public void setValue(AtomicLong value) { + this.value = value; + } + + public long getMax() { + return max; + } + + public void setMax(long max) { + this.max = max; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public SegmentBuffer getBuffer() { + return buffer; + } + + public long getIdle() { + return this.getMax() - getValue().get(); + } + + @Override + public String toString() { + return "Segment(" + "value:" + + value + + ",max:" + + max + + ",step:" + + step + + ")"; + } + + public int getRandomStep() { + return randomStep; + } + + public void setRandomStep(int randomStep) { + this.randomStep = randomStep; + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/SegmentBuffer.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/SegmentBuffer.java new file mode 100644 index 00000000..42e96282 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/segment/model/SegmentBuffer.java @@ -0,0 +1,156 @@ +package com.mall4j.cloud.leaf.segment.model; + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 双buffer + * + * @author left + */ +public class SegmentBuffer { + + private String key; + + /** + * 双buffer + */ + private final Segment[] segments; + + /** + * 当前的使用的segment的index + */ + private volatile int currentPos; + + /** + * 下一个segment是否处于可切换状态 + */ + private volatile boolean nextReady; + + /** + * 是否初始化完成 + */ + private volatile boolean initOk; + + /** + * 线程是否在运行中 + */ + private final AtomicBoolean threadRunning; + + private final ReadWriteLock lock; + + private volatile int step; + + private volatile int minStep; + + private volatile long updateTimestamp; + + public SegmentBuffer() { + segments = new Segment[] { new Segment(this), new Segment(this) }; + currentPos = 0; + nextReady = false; + initOk = false; + threadRunning = new AtomicBoolean(false); + lock = new ReentrantReadWriteLock(); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Segment[] getSegments() { + return segments; + } + + public Segment getCurrent() { + return segments[currentPos]; + } + + public int getCurrentPos() { + return currentPos; + } + + public int nextPos() { + return (currentPos + 1) % 2; + } + + public void switchPos() { + currentPos = nextPos(); + } + + public boolean isInitOk() { + return !initOk; + } + + public void setInitOk(boolean initOk) { + this.initOk = initOk; + } + + public boolean isNextReady() { + return nextReady; + } + + public void setNextReady(boolean nextReady) { + this.nextReady = nextReady; + } + + public AtomicBoolean getThreadRunning() { + return threadRunning; + } + + public Lock rLock() { + return lock.readLock(); + } + + public Lock wLock() { + return lock.writeLock(); + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public int getMinStep() { + return minStep; + } + + public void setMinStep(int minStep) { + this.minStep = minStep; + } + + public long getUpdateTimestamp() { + return updateTimestamp; + } + + public void setUpdateTimestamp(long updateTimestamp) { + this.updateTimestamp = updateTimestamp; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("SegmentBuffer{"); + sb.append("key='").append(key).append('\''); + sb.append(", segments=").append(Arrays.toString(segments)); + sb.append(", currentPos=").append(currentPos); + sb.append(", nextReady=").append(nextReady); + sb.append(", initOk=").append(initOk); + sb.append(", threadRunning=").append(threadRunning); + sb.append(", step=").append(step); + sb.append(", minStep=").append(minStep); + sb.append(", updateTimestamp=").append(updateTimestamp); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/service/SegmentService.java b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/service/SegmentService.java new file mode 100644 index 00000000..0def34e2 --- /dev/null +++ b/mall4cloud-leaf/src/main/java/com/mall4j/cloud/leaf/service/SegmentService.java @@ -0,0 +1,52 @@ +package com.mall4j.cloud.leaf.service; + +import com.mall4j.cloud.leaf.IDGen; +import com.mall4j.cloud.leaf.common.Result; +import com.mall4j.cloud.leaf.exception.InitException; +import com.mall4j.cloud.leaf.segment.SegmentIDGenImpl; +import com.mall4j.cloud.leaf.segment.dao.IDAllocDao; +import com.mall4j.cloud.leaf.segment.dao.impl.IDAllocDaoImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; + +/** + * @author left + */ +@Service("SegmentService") +public class SegmentService { + + private final Logger logger = LoggerFactory.getLogger(SegmentService.class); + + private final IDGen idGen; + + public SegmentService(DataSource dataSource) throws InitException { + // Config Dao + IDAllocDao dao = new IDAllocDaoImpl(dataSource); + + // Config ID Gen + idGen = new SegmentIDGenImpl(); + ((SegmentIDGenImpl) idGen).setDao(dao); + if (idGen.init()) { + logger.info("Segment Service Init Successfully"); + } + else { + throw new InitException("Segment Service Init Fail"); + } + + } + + public Result getId(String key) { + return idGen.get(key); + } + + public SegmentIDGenImpl getIdGen() { + if (idGen instanceof SegmentIDGenImpl) { + return (SegmentIDGenImpl) idGen; + } + return null; + } + +} diff --git a/mall4cloud-leaf/src/main/resources/bootstrap.yml b/mall4cloud-leaf/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..bf656dd8 --- /dev/null +++ b/mall4cloud-leaf/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9100 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-multishop/pom.xml b/mall4cloud-multishop/pom.xml new file mode 100644 index 00000000..e6e7fc56 --- /dev/null +++ b/mall4cloud-multishop/pom.xml @@ -0,0 +1,85 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-multishop + mall4cloud 商家端 + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-search + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-product + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-biz + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/MultishopApplication.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/MultishopApplication.java new file mode 100644 index 00000000..13ad79e5 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/MultishopApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.multishop; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class MultishopApplication { + + public static void main(String[] args) { + SpringApplication.run(MultishopApplication.class, args); + } + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/config/SwaggerConfiguration.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/config/SwaggerConfiguration.java new file mode 100644 index 00000000..63dfd422 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.multishop.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.multishop.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopStatus.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopStatus.java new file mode 100644 index 00000000..8c956e15 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopStatus.java @@ -0,0 +1,45 @@ +package com.mall4j.cloud.multishop.constant; + +/** + * 店铺状态 + * + * @author YXF + */ +public enum ShopStatus { + + /** + * 未开通 + */ + DELETE(-1), + /** + * 停业中 + */ + STOP(0), + + /** + * 营业中 + */ + OPEN(1) + + ; + + private Integer num; + + public Integer value() { + return num; + } + + ShopStatus(Integer num) { + this.num = num; + } + + public static ShopStatus instance(Integer value) { + ShopStatus[] enums = values(); + for (ShopStatus statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopType.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopType.java new file mode 100644 index 00000000..60513644 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/constant/ShopType.java @@ -0,0 +1,39 @@ +package com.mall4j.cloud.multishop.constant; + +/** + * 店铺状态 + * + * @author YXF + */ +public enum ShopType { + + /** + * 自营店 + */ + SELF_SHOP(-1), + /** + * 其他店铺 + */ + STOP(0) + ; + + private Integer num; + + public Integer value() { + return num; + } + + ShopType(Integer num) { + this.num = num; + } + + public static ShopType instance(Integer value) { + ShopType[] enums = values(); + for (ShopType statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/ShopUserAccountController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/ShopUserAccountController.java new file mode 100644 index 00000000..597d4f98 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/ShopUserAccountController.java @@ -0,0 +1,71 @@ +package com.mall4j.cloud.multishop.controller; + +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.dto.ChangeAccountDTO; +import com.mall4j.cloud.multishop.service.ShopUserAccountService; +import com.mall4j.cloud.multishop.service.ShopUserService; +import com.mall4j.cloud.multishop.vo.ShopUserVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/09/02 + */ +@RequestMapping(value = "/shop_user/account") +@RestController +@Api(tags = "店铺用户账号信息") +public class ShopUserAccountController { + + private final ShopUserAccountService shopUserAccountService; + + private final ShopUserService shopUserService; + + public ShopUserAccountController(ShopUserAccountService shopUserAccountService, ShopUserService shopUserService) { + this.shopUserAccountService = shopUserAccountService; + this.shopUserService = shopUserService; + } + + + @GetMapping + @ApiOperation(value = "获取账号信息", notes = "获取账号信息") + public ServerResponseEntity getAccount(Long shopUserId) { + return shopUserAccountService.getByUserIdAndSysType(shopUserId, AuthUserContext.get().getSysType()); + } + + + @PostMapping + @ApiOperation(value = "添加账号", notes = "添加账号") + public ServerResponseEntity addAccount(@Valid @RequestBody ChangeAccountDTO changeAccountDTO) { + ShopUserVO shopUserVO = shopUserService.getByUserId(changeAccountDTO.getUserId()); + if (shopUserVO == null) { + return ServerResponseEntity.showFailMsg("无法获取账户信息"); + } + if (Objects.equals(shopUserVO.getHasAccount(), 1)) { + return ServerResponseEntity.showFailMsg("已有账号,无需重复添加"); + } + if (!Objects.equals(shopUserVO.getShopId(), AuthUserContext.get().getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + return shopUserAccountService.save(changeAccountDTO); + } + + @PutMapping + @ApiOperation(value = "修改账号", notes = "修改账号") + public ServerResponseEntity updateAccount(@Valid @RequestBody ChangeAccountDTO changeAccountDTO) { + ShopUserVO shopUserVO = shopUserService.getByUserId(changeAccountDTO.getUserId()); + if (shopUserVO == null || Objects.equals(shopUserVO.getHasAccount(), 0)) { + return ServerResponseEntity.showFailMsg("无法获取账户信息"); + } + if (!Objects.equals(shopUserVO.getShopId(), AuthUserContext.get().getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + return shopUserAccountService.update(changeAccountDTO); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/HotSearchController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/HotSearchController.java new file mode 100644 index 00000000..cb1caef9 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/HotSearchController.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.multishop.controller.admin; + +import com.mall4j.cloud.multishop.model.HotSearch; +import com.mall4j.cloud.multishop.service.HotSearchService; +import com.mall4j.cloud.multishop.vo.HotSearchVO; +import com.mall4j.cloud.multishop.dto.HotSearchDTO; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +@RestController("adminHotSearchController") +@RequestMapping("/admin/hot_search") +@Api(tags = "admin-热搜") +public class HotSearchController { + + @Autowired + private HotSearchService hotSearchService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "分页获取热搜列表", notes = "分页获取热搜列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, HotSearchDTO hotSearchDTO) { + hotSearchDTO.setShopId(AuthUserContext.get().getTenantId()); + PageVO hotSearchPage = hotSearchService.page(pageDTO, hotSearchDTO); + return ServerResponseEntity.success(hotSearchPage); + } + + @GetMapping + @ApiOperation(value = "获取热搜", notes = "根据hotSearchId获取热搜") + public ServerResponseEntity getByHotSearchId(@RequestParam Long hotSearchId) { + return ServerResponseEntity.success(hotSearchService.getByHotSearchId(hotSearchId)); + } + + @PostMapping + @ApiOperation(value = "保存热搜", notes = "保存热搜") + public ServerResponseEntity save(@Valid @RequestBody HotSearchDTO hotSearchDTO) { + HotSearch hotSearch = mapperFacade.map(hotSearchDTO, HotSearch.class); + hotSearch.setShopId(AuthUserContext.get().getTenantId()); + hotSearchService.save(hotSearch); + hotSearchService.removeHotSearchListCache(hotSearch.getShopId()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新热搜", notes = "更新热搜") + public ServerResponseEntity update(@Valid @RequestBody HotSearchDTO hotSearchDTO) { + HotSearch hotSearch = mapperFacade.map(hotSearchDTO, HotSearch.class); + hotSearch.setShopId(AuthUserContext.get().getTenantId()); + hotSearchService.update(hotSearch); + hotSearchService.removeHotSearchListCache(hotSearch.getShopId()); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除热搜", notes = "根据热搜id删除热搜") + public ServerResponseEntity delete(@RequestParam Long hotSearchId) { + Long shopId = AuthUserContext.get().getTenantId(); + hotSearchService.deleteById(hotSearchId, shopId); + hotSearchService.removeHotSearchListCache(shopId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/IndexImgController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/IndexImgController.java new file mode 100644 index 00000000..0bba9153 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/admin/IndexImgController.java @@ -0,0 +1,86 @@ +package com.mall4j.cloud.multishop.controller.admin; + +import com.mall4j.cloud.api.product.feign.SpuFeignClient; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.dto.IndexImgDTO; +import com.mall4j.cloud.multishop.model.IndexImg; +import com.mall4j.cloud.multishop.service.IndexImgService; +import com.mall4j.cloud.multishop.vo.IndexImgVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +@RestController("adminIndexImgController") +@RequestMapping("/admin/index_img") +@Api(tags = "admin-轮播图") +public class IndexImgController { + + @Autowired + private IndexImgService indexImgService; + @Autowired + private MapperFacade mapperFacade; + @Autowired + private SpuFeignClient spuFeignClient; + + @GetMapping("/page") + @ApiOperation(value = "获取轮播图列表", notes = "分页获取轮播图列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, IndexImgDTO indexImgDTO) { + indexImgDTO.setShopId(AuthUserContext.get().getTenantId()); + PageVO indexImgPage = indexImgService.page(pageDTO, indexImgDTO); + return ServerResponseEntity.success(indexImgPage); + } + + @GetMapping + @ApiOperation(value = "获取轮播图", notes = "根据imgId获取轮播图") + public ServerResponseEntity getByImgId(@RequestParam Long imgId) { + IndexImgVO indexImg = indexImgService.getByImgId(imgId); + if (Objects.nonNull(indexImg.getSpuId())) { + ServerResponseEntity spuResponse = spuFeignClient.getById(indexImg.getSpuId()); + indexImg.setSpu(spuResponse.getData()); + } + return ServerResponseEntity.success(indexImg); + } + + @PostMapping + @ApiOperation(value = "保存轮播图", notes = "保存轮播图") + public ServerResponseEntity save(@Valid @RequestBody IndexImgDTO indexImgDTO) { + IndexImg indexImg = mapperFacade.map(indexImgDTO, IndexImg.class); + indexImg.setImgId(null); + indexImg.setShopId(AuthUserContext.get().getTenantId()); + indexImg.setStatus(StatusEnum.ENABLE.value()); + indexImgService.save(indexImg); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新轮播图", notes = "更新轮播图") + public ServerResponseEntity update(@Valid @RequestBody IndexImgDTO indexImgDTO) { + IndexImg indexImg = mapperFacade.map(indexImgDTO, IndexImg.class); + indexImg.setShopId(AuthUserContext.get().getTenantId()); + indexImgService.update(indexImg); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除轮播图", notes = "根据轮播图id删除轮播图") + public ServerResponseEntity delete(@RequestParam Long imgId) { + indexImgService.deleteById(imgId, AuthUserContext.get().getTenantId()); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/HotSearchController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/HotSearchController.java new file mode 100644 index 00000000..f625f32b --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/HotSearchController.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.multishop.controller.app; + +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.multishop.service.HotSearchService; +import com.mall4j.cloud.multishop.vo.HotSearchVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +@RestController("appHotSearchController") +@RequestMapping("/ua/app/hot_search") +@Api(tags = "app-热搜") +public class HotSearchController { + + @Autowired + private HotSearchService hotSearchService; + + @GetMapping("/list") + @ApiOperation(value = "获取热搜列表", notes = "获取热搜列表") + @ApiImplicitParam(name = "shopId", value = "店铺id", defaultValue = "0") + public ServerResponseEntity> listByShopId(@RequestParam("shopId") Long shopId) { + List hotSearches = hotSearchService.listByShopId(shopId); + return ServerResponseEntity.success(hotSearches); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/IndexImgController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/IndexImgController.java new file mode 100644 index 00000000..af66ea96 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/IndexImgController.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.multishop.controller.app; + +import com.mall4j.cloud.multishop.service.IndexImgService; +import com.mall4j.cloud.multishop.vo.IndexImgVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +@RestController("appIndexImgController") +@RequestMapping("/ua/index_img") +@Api(tags = "app-轮播图") +public class IndexImgController { + + @Autowired + private IndexImgService indexImgService; + + @GetMapping("/list") + @ApiOperation(value = "获取轮播图列表", notes = "分页获取轮播图列表") + @ApiImplicitParam(name = "shopId", value = "店铺id(平台:0)", defaultValue = "0") + public ServerResponseEntity> getList(@RequestParam("shopId") Long shopId) { + List indexImgPage = indexImgService.getListByShopId(shopId); + return ServerResponseEntity.success(indexImgPage); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/MyShopDetailController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/MyShopDetailController.java new file mode 100644 index 00000000..7b246729 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/MyShopDetailController.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.multishop.controller.app; + +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.dto.ShopDetailDTO; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * @Author lth + * @Date 2021/6/30 9:37 + */ +@RequestMapping(value = "/my_shop_detail") +@RestController("appMyShopDetailController") +@Api(tags = "app-我的店铺详情信息") +public class MyShopDetailController { + + @Autowired + private ShopDetailService shopDetailService; + + @PostMapping("/create") + @ApiOperation(value = "创建店铺", notes = "创建店铺") + public ServerResponseEntity create(@Valid @RequestBody ShopDetailDTO shopDetailDTO) { + shopDetailService.createShop(shopDetailDTO); + return ServerResponseEntity.success(); + } + + @GetMapping + @ApiOperation(value = "获取我的店铺", notes = "获取我的店铺") + public ServerResponseEntity get() { + Long shopId = AuthUserContext.get().getTenantId(); + if (Objects.isNull(shopId)) { + return ServerResponseEntity.success(null); + } + return ServerResponseEntity.success(shopDetailService.getByShopId(shopId)); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/ShopDetailController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/ShopDetailController.java new file mode 100644 index 00000000..e4d44a0e --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/app/ShopDetailController.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.multishop.controller.app; + +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import com.mall4j.cloud.multishop.vo.ShopHeadInfoVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Objects; + +/** + * @Author lth + * @Date 2021/6/29 18:39 + */ +@RequestMapping(value = "/ua/shop_detail") +@RestController("appShopDetailController") +@Api(tags = "app-店铺详情信息") +public class ShopDetailController { + + @Autowired + private ShopDetailService shopDetailService; + + @GetMapping("/check_shop_name") + @ApiOperation(value = "验证店铺名称是否重名", notes = "验证店铺名称是否重名") + public ServerResponseEntity checkShopName(@RequestParam("shopName") String shopName) { + Boolean res = shopDetailService.checkShopName(shopName); + return ServerResponseEntity.success(res); + } + + @GetMapping("/head_info") + @ApiOperation(value = "店铺头部信息", notes = "店铺头部信息") + public ServerResponseEntity getShopHeadInfo(Long shopId) { + ShopHeadInfoVO shopHeadInfoVO = new ShopHeadInfoVO(); + ShopDetailVO shopDetailVO = shopDetailService.getByShopId(shopId); + if (Objects.isNull(shopDetailVO)) { + throw new mall4cloudException("店铺不存在"); + } + shopHeadInfoVO.setShopStatus(shopDetailVO.getShopStatus()); + if (!Objects.equals(shopDetailVO.getShopStatus(), 1)) { + return ServerResponseEntity.success(shopHeadInfoVO); + } + shopHeadInfoVO.setShopId(shopId); + shopHeadInfoVO.setType(shopDetailVO.getType()); + shopHeadInfoVO.setIntro(shopDetailVO.getIntro()); + shopHeadInfoVO.setShopLogo(shopDetailVO.getShopLogo()); + shopHeadInfoVO.setShopName(shopDetailVO.getShopName()); + shopHeadInfoVO.setMobileBackgroundPic(shopDetailVO.getMobileBackgroundPic()); + return ServerResponseEntity.success(shopHeadInfoVO); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopDetailController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopDetailController.java new file mode 100644 index 00000000..8083015e --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopDetailController.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.multishop.controller.multishop; + +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Author lth + * @Date 2021/6/24 14:46 + */ +@RequestMapping(value = "/m/shop_detail") +@RestController("multishopShopDetailController") +@Api(tags = "multishop-店铺详情信息") +public class ShopDetailController { + + @Autowired + private ShopDetailService shopDetailService; + + @GetMapping("/info") + @ApiOperation(value = "获取店铺详情信息", notes = "获取店铺详情信息") + public ServerResponseEntity info() { + Long shopId = AuthUserContext.get().getTenantId(); + ShopDetailVO shopDetailVO = shopDetailService.getByShopId(shopId); + return ServerResponseEntity.success(shopDetailVO); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopUserController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopUserController.java new file mode 100644 index 00000000..02f078dd --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/multishop/ShopUserController.java @@ -0,0 +1,103 @@ +package com.mall4j.cloud.multishop.controller.multishop; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.dto.ShopUserDTO; +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import com.mall4j.cloud.multishop.service.ShopUserService; +import com.mall4j.cloud.multishop.vo.ShopUserVO; +import com.mall4j.cloud.multishop.vo.ShopUserSimpleVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/09/02 + */ +@RequestMapping(value = "/m/shop_user") +@RestController("multishopShopUserController") +@Api(tags = "店铺用户信息") +public class ShopUserController { + + @Autowired + private ShopUserService shopUserService; + + @Autowired + private ShopDetailService shopDetailService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/info") + @ApiOperation(value = "登陆店铺用户信息", notes = "获取当前登陆店铺用户的用户信息") + public ServerResponseEntity info() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + ShopUserSimpleVO shopUserSimple = new ShopUserSimpleVO(); + shopUserSimple.setIsAdmin(userInfoInTokenBO.getIsAdmin()); + ShopDetailVO shopDetail = shopDetailService.getByShopId(userInfoInTokenBO.getTenantId()); + shopUserSimple.setAvatar(shopDetail.getShopLogo()); + shopUserSimple.setNickName(shopDetail.getShopName()); + return ServerResponseEntity.success(shopUserSimple); + } + + @GetMapping("/page") + @ApiOperation(value = "店铺用户列表", notes = "获取店铺用户列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, String nickName) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + PageVO shopUserPage = shopUserService.pageByShopId(pageDTO, userInfoInTokenBO.getTenantId(), nickName); + return ServerResponseEntity.success(shopUserPage); + } + + @GetMapping + @ApiOperation(value = "获取店铺用户信息", notes = "根据用户id获取店铺用户信息") + public ServerResponseEntity detail(@RequestParam Long shopUserId) { + return ServerResponseEntity.success(shopUserService.getByUserId(shopUserId)); + } + + @PostMapping + @ApiOperation(value = "保存店铺用户信息", notes = "保存店铺用户信息") + public ServerResponseEntity save(@Valid @RequestBody ShopUserDTO shopUserDTO) { + ShopUser shopUser = mapperFacade.map(shopUserDTO, ShopUser.class); + shopUser.setShopUserId(null); + shopUser.setShopId(AuthUserContext.get().getTenantId()); + shopUser.setHasAccount(0); + shopUserService.save(shopUser,shopUserDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新店铺用户信息", notes = "更新店铺用户信息") + public ServerResponseEntity update(@Valid @RequestBody ShopUserDTO shopUserDTO) { + ShopUser shopUser = mapperFacade.map(shopUserDTO, ShopUser.class); + ShopUserVO dbShopUser = shopUserService.getByUserId(shopUserDTO.getShopUserId()); + if (!Objects.equals(dbShopUser.getShopId(), AuthUserContext.get().getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + shopUser.setShopId(dbShopUser.getShopId()); + shopUserService.update(shopUser,shopUserDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除店铺用户信息", notes = "根据店铺用户id删除店铺用户信息") + public ServerResponseEntity delete(@RequestParam Long shopUserId) { + ShopUserVO dbShopUser = shopUserService.getByUserId(shopUserId); + if (!Objects.equals(dbShopUser.getShopId(), AuthUserContext.get().getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + shopUserService.deleteById(shopUserId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/platform/ShopDetailController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/platform/ShopDetailController.java new file mode 100644 index 00000000..1e309044 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/controller/platform/ShopDetailController.java @@ -0,0 +1,71 @@ +package com.mall4j.cloud.multishop.controller.platform; + +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.dto.ShopDetailDTO; +import com.mall4j.cloud.multishop.model.ShopDetail; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Objects; + +/** + * 店铺详情 + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +@RestController("platformShopDetailController") +@RequestMapping("/platform/shop_detail") +@Api(tags = "platform-店铺信息") +public class ShopDetailController { + + @Autowired + private ShopDetailService shopDetailService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "分页查询", notes = "分页查询") + public ServerResponseEntity> getShopAuditingPage(PageDTO pageDTO, ShopDetailDTO shopDetailDTO) { + if (!Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId())) { + throw new mall4cloudException(ResponseEnum.UNAUTHORIZED); + } + return ServerResponseEntity.success(shopDetailService.page(pageDTO, shopDetailDTO)); + } + + @GetMapping("/info") + @ApiOperation(value = "店铺详情", notes = "店铺详情") + public ServerResponseEntity getInfo(@RequestParam Long shopId) { + ShopDetailVO shopDetailVO = shopDetailService.getByShopId(shopId); + return ServerResponseEntity.success(shopDetailVO); + } + + /** + * 新建店铺 + */ + @PostMapping("/create_shop") + @ApiOperation(value = "新建店铺", notes = "新建店铺") + public ServerResponseEntity createShop(@RequestBody ShopDetailDTO shopDetailDTO) { + shopDetailService.createShop(shopDetailDTO); + return ServerResponseEntity.success(); + } + + @PutMapping("/update_shop") + @ApiOperation(value = "更新店铺", notes = "更新店铺") + public ServerResponseEntity updateShop(@RequestBody ShopDetailDTO shopDetailDTO) { + shopDetailService.update(mapperFacade.map(shopDetailDTO, ShopDetail.class)); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ChangeAccountDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ChangeAccountDTO.java new file mode 100644 index 00000000..92a75bae --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ChangeAccountDTO.java @@ -0,0 +1,95 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020/9/22 + */ +public class ChangeAccountDTO { + + @NotNull(message = "userId not null") + @ApiModelProperty("用户id") + private Long userId; + + @NotBlank(message = "username not blank") + @ApiModelProperty("用户名") + private String username; + + @NotBlank(message = "password not blank") + @ApiModelProperty("密码") + private String password; + + @NotNull(message = "status not null") + @ApiModelProperty("状态 1启用 0禁用") + private Integer status; + + @ApiModelProperty("邮箱") + private String email; + + @ApiModelProperty("手机号") + private String phone; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String toString() { + return "ChangeAccountDTO{" + + "userId=" + userId + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", status=" + status + + ", email='" + email + '\'' + + ", phone='" + phone + '\'' + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/HotSearchDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/HotSearchDTO.java new file mode 100644 index 00000000..c7d4bfd8 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/HotSearchDTO.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; + +/** + * 热搜DTO + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +public class HotSearchDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long hotSearchId; + + @ApiModelProperty("店铺ID 0为全局热搜") + private Long shopId; + + @ApiModelProperty("内容") + private String content; + + @ApiModelProperty("顺序") + private Integer seq; + + @ApiModelProperty("状态 0下线 1上线") + private Integer status; + + @ApiModelProperty("热搜标题") + private String title; + + public Long getHotSearchId() { + return hotSearchId; + } + + public void setHotSearchId(Long hotSearchId) { + this.hotSearchId = hotSearchId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return "HotSearchDTO{" + + "hotSearchId=" + hotSearchId + + ",shopId=" + shopId + + ",content=" + content + + ",seq=" + seq + + ",status=" + status + + ",title=" + title + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/IndexImgDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/IndexImgDTO.java new file mode 100644 index 00000000..265326a9 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/IndexImgDTO.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.NotNull; + +/** + * 轮播图DTO + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +public class IndexImgDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long imgId; + + @ApiModelProperty("店铺ID") + private Long shopId; + + @NotNull(message = "图片不能为空") + @ApiModelProperty("图片") + private String imgUrl; + + @ApiModelProperty("状态") + private Integer status; + + @NotNull(message = "序号不能为空") + @ApiModelProperty("顺序") + private Integer seq; + + @ApiModelProperty("关联商品id") + private Long spuId; + + @NotNull(message = "图片类型不能为空") + @ApiModelProperty("图片类型 0:小程序 1:pc") + private Integer imgType; + + public Long getImgId() { + return imgId; + } + + public void setImgId(Long imgId) { + this.imgId = imgId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getImgType() { + return imgType; + } + + public void setImgType(Integer imgType) { + this.imgType = imgType; + } + + @Override + public String toString() { + return "IndexImgDTO{" + + "imgId=" + imgId + + ",shopId=" + shopId + + ",imgUrl=" + imgUrl + + ",status=" + status + + ",seq=" + seq + + ",spuId=" + spuId + + ",imgType=" + imgType + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopDetailDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopDetailDTO.java new file mode 100644 index 00000000..695cf536 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopDetailDTO.java @@ -0,0 +1,172 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 店铺详情DTO + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public class ShopDetailDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("店铺简介") + private String intro; + + @ApiModelProperty("店铺logo(可修改)") + private String shopLogo; + + @ApiModelProperty("店铺状态(-1:已删除 0: 停业中 1:营业中)") + private Integer shopStatus; + + @ApiModelProperty("营业执照") + private String businessLicense; + + @ApiModelProperty("身份证正面") + private String identityCardFront; + + @ApiModelProperty("身份证反面") + private String identityCardLater; + + @Size(max = 30) + @ApiModelProperty(value = "用户名",required=true) + private String username; + @Size(max = 30) + + @Size(max = 64) + @ApiModelProperty(value = "密码",required=true) + private String password; + + @ApiModelProperty("移动端背景图") + @NotBlank(message="移动端背景图不能为空") + private String mobileBackgroundPic; + + public String getMobileBackgroundPic() { + return mobileBackgroundPic; + } + + public void setMobileBackgroundPic(String mobileBackgroundPic) { + this.mobileBackgroundPic = mobileBackgroundPic; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getIntro() { + return intro; + } + + public void setIntro(String intro) { + this.intro = intro; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + public String getBusinessLicense() { + return businessLicense; + } + + public void setBusinessLicense(String businessLicense) { + this.businessLicense = businessLicense; + } + + public String getIdentityCardFront() { + return identityCardFront; + } + + public void setIdentityCardFront(String identityCardFront) { + this.identityCardFront = identityCardFront; + } + + public String getIdentityCardLater() { + return identityCardLater; + } + + public void setIdentityCardLater(String identityCardLater) { + this.identityCardLater = identityCardLater; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return "ShopDetailDTO{" + + "shopId=" + shopId + + ", type=" + type + + ", shopName='" + shopName + '\'' + + ", intro='" + intro + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", shopStatus=" + shopStatus + + ", businessLicense='" + businessLicense + '\'' + + ", identityCardFront='" + identityCardFront + '\'' + + ", identityCardLater='" + identityCardLater + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", mobileBackgroundPic='" + mobileBackgroundPic + '\'' + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopUserDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopUserDTO.java new file mode 100644 index 00000000..01c925a7 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/ShopUserDTO.java @@ -0,0 +1,80 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/9/8 + */ +public class ShopUserDTO { + + @ApiModelProperty("店铺用户id") + private Long shopUserId; + + @NotBlank(message = "昵称不能为空") + @ApiModelProperty("昵称") + private String nickName; + + @ApiModelProperty("员工编号") + private String code; + + @ApiModelProperty("联系方式") + private String phoneNum; + + @ApiModelProperty("角色id列表") + private List roleIds; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Long getShopUserId() { + return shopUserId; + } + + public void setShopUserId(Long shopUserId) { + this.shopUserId = shopUserId; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + @Override + public String toString() { + return "ShopUserDTO{" + + "shopUserId=" + shopUserId + + ", nickName='" + nickName + '\'' + + ", code='" + code + '\'' + + ", phoneNum='" + phoneNum + '\'' + + ", roleIds=" + roleIds + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/UsernameAndPasswordDTO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/UsernameAndPasswordDTO.java new file mode 100644 index 00000000..2a5b4097 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/dto/UsernameAndPasswordDTO.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.multishop.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * @author lhd + * @date 2020/12/30 + */ +@ApiModel(value= "用户名和密码参数") +public class UsernameAndPasswordDTO { + + @NotBlank(message="用户名不能为空") + @Size(max = 30) + @ApiModelProperty(value = "用户名",required=true) + private String username; + + @NotBlank(message="密码不能为空") + @Size(max = 64) + @ApiModelProperty(value = "密码",required=true) + private String password; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + @Override + public String toString() { + return "UsernameAndPasswordDTO{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", shopId='" + shopId + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/IndexImgFeignController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/IndexImgFeignController.java new file mode 100644 index 00000000..b102b860 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/IndexImgFeignController.java @@ -0,0 +1,24 @@ +package com.mall4j.cloud.multishop.feign; + +import com.mall4j.cloud.api.multishop.feign.IndexImgFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.multishop.service.IndexImgService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Author lth + * @Date 2021/7/8 11:12 + */ +@RestController +public class IndexImgFeignController implements IndexImgFeignClient { + + @Autowired + private IndexImgService indexImgService; + + @Override + public ServerResponseEntity deleteBySpuId(Long spuId, Long shopId) { + indexImgService.deleteBySpuId(spuId, shopId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/ShopDetailFeignController.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/ShopDetailFeignController.java new file mode 100644 index 00000000..f67ee173 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/feign/ShopDetailFeignController.java @@ -0,0 +1,62 @@ +package com.mall4j.cloud.multishop.feign; + +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.api.multishop.feign.ShopDetailFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.multishop.model.ShopDetail; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/11/23 + */ +@RestController +public class ShopDetailFeignController implements ShopDetailFeignClient { + + @Autowired + private ShopDetailService shopDetailService; + @Autowired + private MapperFacade mapperFacade; + + @Override + public ServerResponseEntity getShopNameByShopId(Long shopId) { + ShopDetailVO shopDetail = shopDetailService.getByShopId(shopId); + if (Objects.isNull(shopDetail)) { + return ServerResponseEntity.success(""); + } + return ServerResponseEntity.success(shopDetail.getShopName()); + } + + @Override + public ServerResponseEntity getShopByShopId(Long shopId) { + ShopDetailVO shopDetail = shopDetailService.getByShopId(shopId); + if (Objects.isNull(shopDetail)) { + return ServerResponseEntity.success(new EsShopDetailBO()); + } + return ServerResponseEntity.success(mapperFacade.map(shopDetail, EsShopDetailBO.class)); + } + + + @Override + public ServerResponseEntity> listByShopIds(List shopIds) { + List shopDetail = shopDetailService.listByShopIds(shopIds); + return ServerResponseEntity.success(mapperFacade.mapAsList(shopDetail, ShopDetailVO.class)); + } + + @Override + public ServerResponseEntity shopExtensionData(Long shopId) { + return ServerResponseEntity.success(shopDetailService.shopExtensionData(shopId)); + } + + @Override + public ServerResponseEntity> getShopDetailByShopIdAndShopName(List shopIds, String shopName) { + return ServerResponseEntity.success(shopDetailService.getShopDetailByShopIdAndShopName(shopIds,shopName)); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/HotSearchMapper.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/HotSearchMapper.java new file mode 100644 index 00000000..c82181c6 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/HotSearchMapper.java @@ -0,0 +1,63 @@ +package com.mall4j.cloud.multishop.mapper; + +import com.mall4j.cloud.multishop.dto.HotSearchDTO; +import com.mall4j.cloud.multishop.model.HotSearch; +import com.mall4j.cloud.multishop.vo.HotSearchVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +public interface HotSearchMapper { + + /** + * 获取热搜列表 + * + * @param hotSearchDTO 搜索参数 + * @return 热搜列表 + */ + List list(@Param("hotSearchDTO") HotSearchDTO hotSearchDTO); + + /** + * 根据热搜id获取热搜 + * + * @param hotSearchId 热搜id + * @return 热搜 + */ + HotSearchVO getByHotSearchId(@Param("hotSearchId") Long hotSearchId); + + /** + * 保存热搜 + * + * @param hotSearch 热搜 + */ + void save(@Param("hotSearch") HotSearch hotSearch); + + /** + * 更新热搜 + * + * @param hotSearch 热搜 + */ + void update(@Param("hotSearch") HotSearch hotSearch); + + /** + * 根据热搜id删除热搜 + * + * @param hotSearchId + * @param shopId + */ + void deleteById(@Param("hotSearchId") Long hotSearchId, @Param("shopId") Long shopId); + + /** + * 根据店铺id获取热搜列表 + * + * @param shopId + * @return + */ + List listByShopId(@Param("shopId") Long shopId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/IndexImgMapper.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/IndexImgMapper.java new file mode 100644 index 00000000..e06f1e37 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/IndexImgMapper.java @@ -0,0 +1,63 @@ +package com.mall4j.cloud.multishop.mapper; + +import com.mall4j.cloud.multishop.dto.IndexImgDTO; +import com.mall4j.cloud.multishop.model.IndexImg; +import com.mall4j.cloud.multishop.vo.IndexImgVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +public interface IndexImgMapper { + + /** + * 获取轮播图列表 + * @param indexImgDTO + * @return 轮播图列表 + */ + List list(@Param("indexImg") IndexImgDTO indexImgDTO); + + /** + * 根据轮播图id获取轮播图 + * @param imgId 轮播图id + * @return 轮播图 + */ + IndexImgVO getByImgId(@Param("imgId") Long imgId); + + /** + * 保存轮播图 + * @param indexImg 轮播图 + */ + void save(@Param("indexImg") IndexImg indexImg); + + /** + * 更新轮播图 + * @param indexImg 轮播图 + */ + void update(@Param("indexImg") IndexImg indexImg); + + /** + * 根据轮播图id删除轮播图 + * @param imgId 轮播图id + * @param shopId 店铺id + */ + void deleteByIdAndShopId(@Param("imgId") Long imgId, @Param("shopId") Long shopId); + + /** + * 根据店铺id,获取轮播图列表 + * @param shopId + * @return + */ + List getListByShopId(@Param("shopId") Long shopId); + + /** + * 根据spuId清除轮播图的spuId + * @param spuId + */ + void clearSpuIdBySpuId(Long spuId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopDetailMapper.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopDetailMapper.java new file mode 100644 index 00000000..efe7c48d --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopDetailMapper.java @@ -0,0 +1,111 @@ +package com.mall4j.cloud.multishop.mapper; + +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.multishop.dto.ShopDetailDTO; +import com.mall4j.cloud.multishop.model.ShopDetail; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.multishop.vo.ShopDetailAppVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 店铺详情 + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public interface ShopDetailMapper { + + /** + * 获取店铺详情列表 + * + * @param shopDetailDTO 店铺搜索数据 + * @return 店铺详情列表 + */ + List list(@Param("shopDetail") ShopDetailDTO shopDetailDTO); + + /** + * 根据店铺详情id获取店铺详情 + * + * @param shopId 店铺详情id + * @return 店铺详情 + */ + ShopDetailVO getByShopId(@Param("shopId") Long shopId); + + /** + * 保存店铺详情 + * + * @param shopDetail 店铺详情 + */ + void save(@Param("shopDetail") ShopDetail shopDetail); + + /** + * 更新店铺详情 + * + * @param shopDetail 店铺详情 + */ + void update(@Param("shopDetail") ShopDetail shopDetail); + + /** + * 根据店铺详情id删除店铺详情 + * + * @param shopId + */ + void deleteById(@Param("shopId") Long shopId); + + /** + * 根据店铺id列表,获取店铺信息 + * @param shopIds 店铺id列表 + * @return 店铺列表 + */ + List listByShopIds(@Param("shopIds") List shopIds); + + /** + * 店铺搜索列表 + * + * @param shopDetailDTO + * @return + */ + List shopSearchList(@Param("shopDetail") ShopDetailDTO shopDetailDTO); + + /** + * 统计该店铺名被其他用户使用的数量 + * + * @param shopName + * @param shopId + * @return + */ + int countShopName(@Param("shopName") String shopName, @Param("shopId") Long shopId); + + /** + * 改变店铺状态 + * + * @param shopId + * @param shopStatus + */ + void changeSpuStatus(@Param("shopId") Long shopId, @Param("shopStatus") Integer shopStatus); + + /** + * 获取店铺信息及扩展信息 + * + * @param shopId + * @return + */ + EsShopDetailBO shopExtensionData(@Param("shopId") Long shopId); + + /** + * 获取店铺信息及扩展信息 + * @param shopIds 店铺ids + * @param shopName 店铺名称 + * @return 店铺信息列表 + */ + List getShopDetailByShopIdAndShopName(@Param("shopIds") List shopIds, @Param("shopName") String shopName); + + /** + * 根据店铺id获取店铺扩展信息 + * @param shopId 店铺id + * @return 店铺扩展信息 + */ + ShopDetailVO getShoExtensionsByShopId(Long shopId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopUserMapper.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopUserMapper.java new file mode 100644 index 00000000..93a6cdfc --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/mapper/ShopUserMapper.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.multishop.mapper; + +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.vo.ShopUserVO; +import com.mall4j.cloud.multishop.vo.ShopUserSimpleVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +public interface ShopUserMapper { + + /** + * 根据用户id获取当前登陆的商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + ShopUserSimpleVO getSimpleByUserId(@Param("userId") Long userId); + + /** + * 获取店铺用户列表 + * + * @param shopId 店铺id + * @param nickName 昵称 + * @return 店铺用户列表 + */ + List listByShopId(@Param("shopId") Long shopId, @Param("nickName") String nickName); + + /** + * 根据用户id获取商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + ShopUserVO getByUserId(@Param("userId") Long userId); + + /** + * 保存商家用户信息 + * + * @param shopUser + */ + void save(@Param("shopUser") ShopUser shopUser); + + /** + * 更新店铺用户信息 + * + * @param shopUser + */ + void update(@Param("shopUser") ShopUser shopUser); + + /** + * 根据店铺用户id删除店铺用户 + * + * @param shopUserId + */ + void deleteById(@Param("shopUserId") Long shopUserId); + + /** + * 获取店主账号的用户id-第一个创建的账号(仅用于审核店铺) + * + * @param shopId + * @return + */ + Long getUserIdByShopId(@Param("shopId") Long shopId); + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/HotSearch.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/HotSearch.java new file mode 100644 index 00000000..9a0aebca --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/HotSearch.java @@ -0,0 +1,106 @@ +package com.mall4j.cloud.multishop.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +public class HotSearch extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long hotSearchId; + + /** + * 店铺ID 0为全局热搜 + */ + private Long shopId; + + /** + * 内容 + */ + private String content; + + /** + * 顺序 + */ + private Integer seq; + + /** + * 状态 0下线 1上线 + */ + private Integer status; + + /** + * 热搜标题 + */ + private String title; + + public Long getHotSearchId() { + return hotSearchId; + } + + public void setHotSearchId(Long hotSearchId) { + this.hotSearchId = hotSearchId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return "HotSearch{" + + "hotSearchId=" + hotSearchId + + ", shopId=" + shopId + + ", content='" + content + '\'' + + ", seq=" + seq + + ", status=" + status + + ", title='" + title + '\'' + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/IndexImg.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/IndexImg.java new file mode 100644 index 00000000..62d58067 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/IndexImg.java @@ -0,0 +1,119 @@ +package com.mall4j.cloud.multishop.model; + +import java.io.Serializable; +import com.mall4j.cloud.common.model.BaseModel; +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +public class IndexImg extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long imgId; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 图片 + */ + private String imgUrl; + + /** + * 状态 + */ + private Integer status; + + /** + * 顺序 + */ + private Integer seq; + + /** + * 关联商品id + */ + private Long spuId; + + /** + * 图片类型 0:小程序 1:pc + */ + private Integer imgType; + + public Long getImgId() { + return imgId; + } + + public void setImgId(Long imgId) { + this.imgId = imgId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getImgType() { + return imgType; + } + + public void setImgType(Integer imgType) { + this.imgType = imgType; + } + + @Override + public String toString() { + return "IndexImg{" + + "imgId=" + imgId + + ",shopId=" + shopId + + ",imgUrl=" + imgUrl + + ",status=" + status + + ",seq=" + seq + + ",spuId=" + spuId + + ",imgType=" + imgType + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopDetail.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopDetail.java new file mode 100644 index 00000000..5c045c47 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopDetail.java @@ -0,0 +1,174 @@ +package com.mall4j.cloud.multishop.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 店铺详情 + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public class ShopDetail extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 店铺类型1自营店 2普通店 + */ + private Integer type; + + /** + * 店铺名称 + */ + private String shopName; + + /** + * 店铺简介 + */ + private String intro; + + /** + * 店铺logo(可修改) + */ + private String shopLogo; + + /** + * 店铺状态(-1:已删除 0: 停业中 1:营业中) + */ + private Integer shopStatus; + + /** + * 营业执照 + */ + private String businessLicense; + + /** + * 身份证正面 + */ + private String identityCardFront; + + /** + * 身份证反面 + */ + private String identityCardLater; + + /** + * 移动端背景图 + */ + private String mobileBackgroundPic; + + /** + * pc背景图 + */ + private String pcBackgroundPic; + + public String getMobileBackgroundPic() { + return mobileBackgroundPic; + } + + public void setMobileBackgroundPic(String mobileBackgroundPic) { + this.mobileBackgroundPic = mobileBackgroundPic; + } + + public String getPcBackgroundPic() { + return pcBackgroundPic; + } + + public void setPcBackgroundPic(String pcBackgroundPic) { + this.pcBackgroundPic = pcBackgroundPic; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getIntro() { + return intro; + } + + public void setIntro(String intro) { + this.intro = intro; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + public String getBusinessLicense() { + return businessLicense; + } + + public void setBusinessLicense(String businessLicense) { + this.businessLicense = businessLicense; + } + + public String getIdentityCardFront() { + return identityCardFront; + } + + public void setIdentityCardFront(String identityCardFront) { + this.identityCardFront = identityCardFront; + } + + public String getIdentityCardLater() { + return identityCardLater; + } + + public void setIdentityCardLater(String identityCardLater) { + this.identityCardLater = identityCardLater; + } + + @Override + public String toString() { + return "ShopDetail{" + + "shopId=" + shopId + + ", type=" + type + + ", shopName='" + shopName + '\'' + + ", intro='" + intro + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", shopStatus=" + shopStatus + + ", businessLicense='" + businessLicense + '\'' + + ", identityCardFront='" + identityCardFront + '\'' + + ", identityCardLater='" + identityCardLater + '\'' + + ", mobileBackgroundPic='" + mobileBackgroundPic + '\'' + + ", pcBackgroundPic='" + pcBackgroundPic + '\'' + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopUser.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopUser.java new file mode 100644 index 00000000..7e7b91d5 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/model/ShopUser.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.multishop.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.database.annotations.DistributedId; +import com.mall4j.cloud.common.model.BaseModel; +/** + * 商家用户 + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public class ShopUser extends BaseModel implements Serializable{ + + /** + * 商家用户id + */ + @DistributedId("mall4cloud-multishop-user") + private Long shopUserId; + + /** + * 关联店铺id + */ + private Long shopId; + + /** + * 昵称 + */ + private String nickName; + + /** + * 员工编号 + */ + private String code; + + /** + * 联系方式 + */ + private String phoneNum; + + /** + * 是否已经设置账号 + */ + private Integer hasAccount; + + public Long getShopUserId() { + return shopUserId; + } + + public void setShopUserId(Long shopUserId) { + this.shopUserId = shopUserId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Integer getHasAccount() { + return hasAccount; + } + + public void setHasAccount(Integer hasAccount) { + this.hasAccount = hasAccount; + } + + @Override + public String toString() { + return "ShopUser{" + + "shopUserId=" + shopUserId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",shopId=" + shopId + + ",nickName=" + nickName + + ",code=" + code + + ",phoneNum=" + phoneNum + + ",hasAccount=" + hasAccount + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/HotSearchService.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/HotSearchService.java new file mode 100644 index 00000000..299fd1b3 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/HotSearchService.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.multishop.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.dto.HotSearchDTO; +import com.mall4j.cloud.multishop.model.HotSearch; +import com.mall4j.cloud.multishop.vo.HotSearchVO; + +import java.util.List; + +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +public interface HotSearchService { + + /** + * 分页获取热搜列表 + * @param pageDTO 分页参数 + * @param hotSearchDTO 搜索参数 + * @return 热搜列表分页数据 + */ + PageVO page(PageDTO pageDTO, HotSearchDTO hotSearchDTO); + + /** + * 根据热搜id获取热搜 + * + * @param hotSearchId 热搜id + * @return 热搜 + */ + HotSearchVO getByHotSearchId(Long hotSearchId); + + /** + * 保存热搜 + * @param hotSearch 热搜 + */ + void save(HotSearch hotSearch); + + /** + * 更新热搜 + * @param hotSearch 热搜 + */ + void update(HotSearch hotSearch); + + /** + * 根据热搜id删除热搜 + * @param hotSearchId 热搜id + * @param shopId 店铺id + */ + void deleteById(Long hotSearchId, Long shopId); + + /** + * 获取热搜列表 + * @param shopId + * @return + */ + List listByShopId(Long shopId); + + /** + * 清除热搜列表缓存 + * @param shopId + */ + void removeHotSearchListCache(Long shopId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/IndexImgService.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/IndexImgService.java new file mode 100644 index 00000000..1bc46bb9 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/IndexImgService.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.multishop.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.dto.IndexImgDTO; +import com.mall4j.cloud.multishop.model.IndexImg; +import com.mall4j.cloud.multishop.vo.IndexImgVO; + +import java.util.List; + +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +public interface IndexImgService { + + /** + * 分页获取轮播图列表 + * @param pageDTO 分页参数 + * @param indexImgDTO + * @return 轮播图列表分页数据 + */ + PageVO page(PageDTO pageDTO, IndexImgDTO indexImgDTO); + + /** + * 根据轮播图id获取轮播图 + * @param imgId 轮播图id + * @return 轮播图 + */ + IndexImgVO getByImgId(Long imgId); + + /** + * 保存轮播图 + * @param indexImg 轮播图 + */ + void save(IndexImg indexImg); + + /** + * 更新轮播图 + * @param indexImg 轮播图 + */ + void update(IndexImg indexImg); + + /** + * 根据轮播图id删除轮播图 + * @param imgId + * @param shopId + */ + void deleteById(Long imgId, Long shopId); + + /** + * 根据店铺id,获取轮播图列表 + * @param shopId + * @return + */ + List getListByShopId(Long shopId); + + /** + * 根据商品d删除轮播图信息 + * @param spuId + * @param shopId + */ + void deleteBySpuId(Long spuId, Long shopId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopDetailService.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopDetailService.java new file mode 100644 index 00000000..331324ed --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopDetailService.java @@ -0,0 +1,123 @@ +package com.mall4j.cloud.multishop.service; + +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.dto.ShopDetailDTO; +import com.mall4j.cloud.multishop.model.ShopDetail; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.multishop.vo.ShopDetailAppVO; + +import java.util.List; + +/** + * 店铺详情 + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public interface ShopDetailService { + + /** + * 分页获取店铺详情列表 + * + * @param pageDTO 分页参数 + * @param shopDetailDTO 店铺搜索数据 + * @return 店铺详情列表分页数据 + */ + PageVO page(PageDTO pageDTO, ShopDetailDTO shopDetailDTO); + + /** + * 根据店铺详情id获取店铺详情 + * + * @param shopId 店铺详情id + * @return 店铺详情 + */ + ShopDetailVO getByShopId(Long shopId); + + /** + * 保存店铺详情 + * @param shopDetail 店铺详情 + */ + void save(ShopDetail shopDetail); + + /** + * 更新店铺详情 + * @param shopDetail 店铺详情 + */ + void update(ShopDetail shopDetail); + + /** + * 根据店铺详情id删除店铺详情 + * @param shopId + */ + void deleteById(Long shopId); + + /** + * 获取店铺列表信息 + * @param shopIds + * @return + */ + List listByShopIds(List shopIds); + + /** + * 申请开店 + * + * @param shopDetailDTO 申请开店信息 + */ + void applyShop(ShopDetailDTO shopDetailDTO); + + /** + * 店铺搜索分页 + * + * @param pageDTO 分页参数 + * @param shopDetailDTO 店铺信息 + * @return 店铺信息列表 + */ + PageVO shopSearchPage(PageDTO pageDTO, ShopDetailDTO shopDetailDTO); + + /** + * 改变店铺状态 + * + * @param shopId + * @param shopStatus + */ + void changeSpuStatus(Long shopId, Integer shopStatus); + + /** + * 删除店铺缓存 + * + * @param shopId + */ + void removeCacheByShopId(Long shopId); + + /** + * 获取店铺信息及扩展信息 + * + * @param shopId + * @return + */ + EsShopDetailBO shopExtensionData(Long shopId); + + /** + * 创建店铺 + * + * @param shopDetailDTO + */ + void createShop(ShopDetailDTO shopDetailDTO); + + /** + * 获取店铺信息及扩展信息 + * @param shopIds 店铺ids + * @param shopName 店铺名称 + * @return 店铺信息列表 + */ + List getShopDetailByShopIdAndShopName(List shopIds, String shopName); + + /** + * 验证店铺名称是否重名 + * @param shopName + * @return + */ + Boolean checkShopName(String shopName); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserAccountService.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserAccountService.java new file mode 100644 index 00000000..c84213fb --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserAccountService.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.multishop.service; + +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.multishop.dto.ChangeAccountDTO; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +public interface ShopUserAccountService { + + /** + * 添加账户 + * @param changeAccountDTO 账户信息 + * @return void + */ + ServerResponseEntity save(ChangeAccountDTO changeAccountDTO); + + /** + * 更新账户 + * @param changeAccountDTO 账户信息 + * @return + */ + ServerResponseEntity update(ChangeAccountDTO changeAccountDTO); + + /** + * 根据用户id和系统类型获取用户信息 + * @param userId 用户id + * @param sysType 系统类型 + * @return void + */ + ServerResponseEntity getByUserIdAndSysType(Long userId, Integer sysType); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserService.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserService.java new file mode 100644 index 00000000..af811ba5 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/ShopUserService.java @@ -0,0 +1,59 @@ +package com.mall4j.cloud.multishop.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.vo.ShopUserVO; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +public interface ShopUserService { + + /** + * 分页获取店铺用户列表 + * @param pageDTO 分页参数 + * @param shopId 店铺id + * @param nickName 昵称 + * @return 店铺用户列表 + */ + PageVO pageByShopId(PageDTO pageDTO, Long shopId, String nickName); + + /** + * 根据用户id获取商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + ShopUserVO getByUserId(Long userId); + + /** + * 保存店铺用户信息 + * @param shopUser 店铺用户id + * @param roleIds 角色id列表 + */ + void save(ShopUser shopUser, List roleIds); + + /** + * 更新店铺用户信息 + * @param shopUser 店铺用户id + * @param roleIds 角色id列表 + */ + void update(ShopUser shopUser,List roleIds); + + /** + * 根据店铺用户id删除店铺用户信息 + * @param shopUserId 店铺用户id + */ + void deleteById(Long shopUserId); + + /** + * 获取店主账号的用户id-第一个创建的账号(仅用于审核店铺) + * @param shopId + * @return + */ + Long getUserIdByShopId(Long shopId); +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/HotSearchServiceImpl.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/HotSearchServiceImpl.java new file mode 100644 index 00000000..eaa5079d --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/HotSearchServiceImpl.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.multishop.service.impl; + +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.dto.HotSearchDTO; +import com.mall4j.cloud.multishop.model.HotSearch; +import com.mall4j.cloud.multishop.mapper.HotSearchMapper; +import com.mall4j.cloud.multishop.service.HotSearchService; +import com.mall4j.cloud.multishop.vo.HotSearchVO; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * 热搜 + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +@Service +public class HotSearchServiceImpl implements HotSearchService { + + @Autowired + private HotSearchMapper hotSearchMapper; + + @Override + public PageVO page(PageDTO pageDTO, HotSearchDTO hotSearchDTO) { + return PageUtil.doPage(pageDTO, () -> hotSearchMapper.list(hotSearchDTO)); + } + + @Override + public HotSearchVO getByHotSearchId(Long hotSearchId) { + return hotSearchMapper.getByHotSearchId(hotSearchId); + } + + @Override + public void save(HotSearch hotSearch) { + hotSearchMapper.save(hotSearch); + } + + @Override + public void update(HotSearch hotSearch) { + hotSearchMapper.update(hotSearch); + } + + @Override + public void deleteById(Long hotSearchId, Long shopId) { + hotSearchMapper.deleteById(hotSearchId, shopId); + } + + @Override + @Cacheable(cacheNames = CacheNames.HOT_SEARCH_LIST_KEY, key = "#shopId") + public List listByShopId(Long shopId) { + return hotSearchMapper.listByShopId(shopId); + } + + @Override + @CacheEvict(cacheNames = CacheNames.HOT_SEARCH_LIST_KEY, key = "#shopId") + public void removeHotSearchListCache(Long shopId) { + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/IndexImgServiceImpl.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/IndexImgServiceImpl.java new file mode 100644 index 00000000..9b6d0c70 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/IndexImgServiceImpl.java @@ -0,0 +1,76 @@ +package com.mall4j.cloud.multishop.service.impl; + +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.multishop.dto.IndexImgDTO; +import com.mall4j.cloud.multishop.model.IndexImg; +import com.mall4j.cloud.multishop.mapper.IndexImgMapper; +import com.mall4j.cloud.multishop.service.IndexImgService; +import com.mall4j.cloud.multishop.vo.IndexImgVO; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * 轮播图 + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +@Service +public class IndexImgServiceImpl implements IndexImgService { + + @Autowired + private IndexImgMapper indexImgMapper; + + @Override + public PageVO page(PageDTO pageDTO, IndexImgDTO indexImgDTO) { + return PageUtil.doPage(pageDTO, () -> indexImgMapper.list(indexImgDTO)); + } + + @Override + public IndexImgVO getByImgId(Long imgId) { + return indexImgMapper.getByImgId(imgId); + } + + @Override + @CacheEvict(cacheNames = CacheNames.INDEX_IMG_KEY, key = "#indexImg.shopId") + public void save(IndexImg indexImg) { + indexImgMapper.save(indexImg); + } + + @Override + @CacheEvict(cacheNames = CacheNames.INDEX_IMG_KEY, key = "#indexImg.shopId") + public void update(IndexImg indexImg) { + indexImgMapper.update(indexImg); + } + + @Override + @CacheEvict(cacheNames = CacheNames.INDEX_IMG_KEY, key = "#shopId") + public void deleteById(Long imgId, Long shopId) { + indexImgMapper.deleteByIdAndShopId(imgId, shopId); + } + + @Override + @Cacheable(cacheNames = CacheNames.INDEX_IMG_KEY, key = "#shopId", sync = true) + public List getListByShopId(Long shopId) { + List listByShopId = indexImgMapper.getListByShopId(shopId); + return listByShopId; + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.INDEX_IMG_KEY, key = "#shopId"), + @CacheEvict(cacheNames = CacheNames.INDEX_IMG_KEY, key = "0") + }) + public void deleteBySpuId(Long spuId, Long shopId) { + indexImgMapper.clearSpuIdBySpuId(spuId); + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopDetailServiceImpl.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopDetailServiceImpl.java new file mode 100644 index 00000000..bbf377b1 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopDetailServiceImpl.java @@ -0,0 +1,272 @@ +package com.mall4j.cloud.multishop.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.api.feign.SearchSpuFeignClient; +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.api.vo.search.SpuSearchVO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.constant.UserAdminType; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.IpHelper; +import com.mall4j.cloud.common.util.PrincipalUtil; +import com.mall4j.cloud.multishop.constant.ShopStatus; +import com.mall4j.cloud.multishop.constant.ShopType; +import com.mall4j.cloud.multishop.dto.ShopDetailDTO; +import com.mall4j.cloud.multishop.mapper.ShopDetailMapper; +import com.mall4j.cloud.multishop.model.ShopDetail; +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.service.ShopDetailService; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.multishop.service.ShopUserService; +import com.mall4j.cloud.multishop.vo.ShopDetailAppVO; +import io.seata.spring.annotation.GlobalTransactional; +import ma.glasnost.orika.MapperFacade; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 店铺详情 + * + * @author FrozenWatermelon + * @date 2020-11-23 16:24:29 + */ +@Service +public class ShopDetailServiceImpl implements ShopDetailService { + + @Autowired + private ShopDetailMapper shopDetailMapper; + @Autowired + private SearchSpuFeignClient searchSpuFeignClient; + @Autowired + private ShopUserService shopUserService; + @Autowired + private MapperFacade mapperFacade; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private AccountFeignClient accountFeignClient; + + @Override + public PageVO page(PageDTO pageDTO, ShopDetailDTO shopDetailDTO) { + return PageUtil.doPage(pageDTO, () -> shopDetailMapper.list(shopDetailDTO)); + } + + @Override + @Cacheable(cacheNames = CacheNames.SHOP_DETAIL_ID_KEY, key = "#shopId") + public ShopDetailVO getByShopId(Long shopId) { + ServerResponseEntity accountRes = accountFeignClient.getMerchantInfoByTenantId(shopId); + if (!accountRes.isSuccess()) { + throw new mall4cloudException("商家信息获取失败"); + } + AuthAccountVO authAccountVO = accountRes.getData(); + ShopDetailVO shopDetailVO = shopDetailMapper.getByShopId(shopId); + if (Objects.nonNull(authAccountVO)) { + shopDetailVO.setUsername(authAccountVO.getUsername()); + } + return shopDetailVO; + } + + @Override + public void save(ShopDetail shopDetail) { + shopDetailMapper.save(shopDetail); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_DETAIL_ID_KEY, key = "#shopDetail.shopId") + public void update(ShopDetail shopDetail) { + shopDetailMapper.update(shopDetail); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_DETAIL_ID_KEY, key = "#shopId") + public void deleteById(Long shopId) { + shopDetailMapper.deleteById(shopId); + } + + @Override + public List listByShopIds(List shopIds) { + if (CollUtil.isEmpty(shopIds)) { + return new ArrayList<>(); + } + return shopDetailMapper.listByShopIds(shopIds); + } + + @Override + public PageVO shopSearchPage(PageDTO pageDTO, ShopDetailDTO shopDetailDTO) { + PageVO page = PageUtil.doPage(pageDTO, () -> shopDetailMapper.shopSearchList(shopDetailDTO)); + Set spuIdSet = page.getList().stream().map(ShopDetailAppVO::getShopId).collect(Collectors.toSet()); + ServerResponseEntity> spuResponse = searchSpuFeignClient.limitSizeListByShopIds(new ArrayList<>(spuIdSet), Constant.SPU_SIZE_FIVE); + if (!Objects.equals(spuResponse.getCode(), ResponseEnum.OK.value())) { + throw new mall4cloudException(spuResponse.getMsg()); + } else if (CollectionUtil.isEmpty(spuResponse.getData())) { + return page; + } + List data = spuResponse.getData(); + Map> shopMap = data.stream().collect(Collectors.groupingBy(SpuSearchVO::getShopId)); + for (ShopDetailAppVO shopDetail : page.getList()) { + shopDetail.setSpuList(shopMap.get(shopDetail.getShopId())); + } + return page; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @GlobalTransactional + public void applyShop(ShopDetailDTO shopDetailDTO) { + checkShopInfo(shopDetailDTO); + ShopDetail newShopDetail = mapperFacade.map(shopDetailDTO, ShopDetail.class); + // 申请开店 + newShopDetail.setShopStatus(ShopStatus.OPEN.value()); + newShopDetail.setType(ShopType.STOP.value()); + shopDetailMapper.save(newShopDetail); + shopDetailDTO.setShopId(newShopDetail.getShopId()); + // 创建账号 + createShopAccount(shopDetailDTO, StatusEnum.ENABLE); + } + + @Override + public void changeSpuStatus(Long shopId, Integer shopStatus) { + shopDetailMapper.changeSpuStatus(shopId, shopStatus); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_DETAIL_ID_KEY, key = "#shopId") + public void removeCacheByShopId(Long shopId) { + + } + + @Override + public EsShopDetailBO shopExtensionData(Long shopId) { + return shopDetailMapper.shopExtensionData(shopId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @GlobalTransactional(rollbackFor = Exception.class) + public void createShop(ShopDetailDTO shopDetailDTO) { + checkShopInfo(shopDetailDTO); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + if (Objects.nonNull(userInfoInTokenBO.getTenantId())) { + throw new mall4cloudException("该用户已经创建过店铺"); + } + // 保存店铺 + ShopDetail shopDetail = mapperFacade.map(shopDetailDTO, ShopDetail.class); + shopDetail.setShopStatus(ShopStatus.OPEN.value()); + shopDetailMapper.save(shopDetail); + // 保存商家账号 + // 保存到shopUser + ShopUser shopUser = new ShopUser(); + shopUser.setShopId(shopDetail.getShopId()); + shopUser.setHasAccount(1); + shopUser.setNickName(shopDetailDTO.getShopName()); + shopUserService.save(shopUser, null); + // 保存到authAccount + AuthAccountDTO authAccountDTO = new AuthAccountDTO(); + authAccountDTO.setTenantId(shopDetail.getShopId()); + authAccountDTO.setUsername(shopDetailDTO.getUsername()); + authAccountDTO.setPassword(shopDetailDTO.getPassword()); + authAccountDTO.setCreateIp(IpHelper.getIpAddr()); + authAccountDTO.setStatus(StatusEnum.ENABLE.value()); + authAccountDTO.setSysType(SysTypeEnum.MULTISHOP.value()); + authAccountDTO.setIsAdmin(UserAdminType.ADMIN.value()); + authAccountDTO.setUserId(shopUser.getShopUserId()); + accountFeignClient.save(authAccountDTO); + + userInfoInTokenBO.setTenantId(shopDetail.getShopId()); + ServerResponseEntity updateTenantIdRes = accountFeignClient.updateUserInfoByUserIdAndSysType(userInfoInTokenBO, AuthUserContext.get().getUserId(), SysTypeEnum.ORDINARY.value()); + if (!Objects.equals(updateTenantIdRes.getCode(), ResponseEnum.OK.value())) { + throw new mall4cloudException(updateTenantIdRes.getMsg()); + } + } + + @Override + public List getShopDetailByShopIdAndShopName(List shopIds, String shopName) { + return shopDetailMapper.getShopDetailByShopIdAndShopName(shopIds,shopName); + } + + @Override + public Boolean checkShopName(String shopName) { + int count = shopDetailMapper.countShopName(shopName, null); + return count <= 0; + } + + /** + * 检验店铺信息是否正确 + * @param shopDetailDTO + */ + private void checkShopInfo(ShopDetailDTO shopDetailDTO) { + // 店铺名称 + if (StrUtil.isNotBlank(shopDetailDTO.getShopName())) { + shopDetailDTO.setShopName(shopDetailDTO.getShopName().trim()); + } + if(shopDetailMapper.countShopName(shopDetailDTO.getShopName(), null) > 0) { + throw new mall4cloudException("店铺名称已存在"); + } + + String username = shopDetailDTO.getUsername(); + // 用户名 + if (!PrincipalUtil.isUserName(username)) { + throw new mall4cloudException("用户名格式不正确"); + } + + ServerResponseEntity accountResponse = accountFeignClient.getByUsernameAndSysType(username, SysTypeEnum.MULTISHOP); + if (!Objects.equals(accountResponse.getCode(), ResponseEnum.OK.value())) { + throw new mall4cloudException(accountResponse.getMsg()); + } + + AuthAccountVO authAccountVO = accountResponse.getData(); + if (Objects.nonNull(authAccountVO)) { + throw new mall4cloudException("用户账号已存在"); + } + } + + /** + * 创建店铺初始账号 + * @param shopDetailDTO + * @param statusEnum + */ + public void createShopAccount(ShopDetailDTO shopDetailDTO, StatusEnum statusEnum) { + ShopUser shopUser = new ShopUser(); + shopUser.setShopId(shopDetailDTO.getShopId()); + shopUser.setHasAccount(1); + shopUser.setNickName(shopDetailDTO.getUsername()); + shopUserService.save(shopUser, null); + + AuthAccountDTO authAccountDTO = new AuthAccountDTO(); + authAccountDTO.setUsername(shopDetailDTO.getUsername()); + String password = passwordEncoder.encode(shopDetailDTO.getPassword().trim()); + authAccountDTO.setPassword(password); + authAccountDTO.setStatus(statusEnum.value()); + authAccountDTO.setSysType(SysTypeEnum.MULTISHOP.value()); + authAccountDTO.setCreateIp(IpHelper.getIpAddr()); + authAccountDTO.setTenantId(shopDetailDTO.getShopId()); + authAccountDTO.setUserId(shopUser.getShopUserId()); + authAccountDTO.setIsAdmin(UserAdminType.ADMIN.value()); + ServerResponseEntity save = accountFeignClient.save(authAccountDTO); + if (!Objects.equals(save.getCode(), ResponseEnum.OK.value())) { + throw new mall4cloudException(save.getMsg()); + } + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserAccountServiceImpl.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserAccountServiceImpl.java new file mode 100644 index 00000000..ff423d0b --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserAccountServiceImpl.java @@ -0,0 +1,83 @@ +package com.mall4j.cloud.multishop.service.impl; + +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.IpHelper; +import com.mall4j.cloud.multishop.dto.ChangeAccountDTO; +import com.mall4j.cloud.multishop.mapper.ShopUserMapper; +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.service.ShopUserAccountService; +import io.seata.spring.annotation.GlobalTransactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +@Service +public class ShopUserAccountServiceImpl implements ShopUserAccountService { + + @Resource + private ShopUserMapper shopUserMapper; + @Autowired + private AccountFeignClient accountFeignClient; + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity save(ChangeAccountDTO changeAccountDTO) { + AuthAccountDTO authAccountDTO = getAuthAccountDTO(changeAccountDTO); + authAccountDTO.setCreateIp(IpHelper.getIpAddr()); + authAccountDTO.setIsAdmin(0); + // 保存 + ServerResponseEntity serverResponseEntity = accountFeignClient.save(authAccountDTO); + if (!serverResponseEntity.isSuccess()) { + return ServerResponseEntity.transform(serverResponseEntity); + } + ShopUser shopUser = new ShopUser(); + shopUser.setShopUserId(changeAccountDTO.getUserId()); + shopUser.setHasAccount(1); + shopUser.setShopId(AuthUserContext.get().getTenantId()); + shopUserMapper.update(shopUser); + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity update(ChangeAccountDTO changeAccountDTO) { + + AuthAccountDTO authAccountDTO = getAuthAccountDTO(changeAccountDTO); + // 更新,不涉及分布式事务 + ServerResponseEntity serverResponseEntity = accountFeignClient.update(authAccountDTO); + if (!serverResponseEntity.isSuccess()) { + return serverResponseEntity; + } + + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity getByUserIdAndSysType(Long userId, Integer sysType) { + return accountFeignClient.getByUserIdAndSysType(userId,sysType); + } + + private AuthAccountDTO getAuthAccountDTO(ChangeAccountDTO changeAccountDTO) { + AuthAccountDTO authAccountDTO = new AuthAccountDTO(); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + authAccountDTO.setPassword(changeAccountDTO.getPassword()); + authAccountDTO.setUsername(changeAccountDTO.getUsername()); + authAccountDTO.setStatus(changeAccountDTO.getStatus()); + authAccountDTO.setSysType(userInfoInTokenBO.getSysType()); + authAccountDTO.setTenantId(userInfoInTokenBO.getTenantId()); + authAccountDTO.setUserId(changeAccountDTO.getUserId()); + return authAccountDTO; + } + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserServiceImpl.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserServiceImpl.java new file mode 100644 index 00000000..a583bf45 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/service/impl/ShopUserServiceImpl.java @@ -0,0 +1,94 @@ +package com.mall4j.cloud.multishop.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.api.rbac.dto.UserRoleDTO; +import com.mall4j.cloud.api.rbac.feign.UserRoleFeignClient; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.multishop.mapper.ShopUserMapper; +import com.mall4j.cloud.multishop.model.ShopUser; +import com.mall4j.cloud.multishop.service.ShopUserService; +import com.mall4j.cloud.multishop.vo.ShopUserVO; +import io.seata.spring.annotation.GlobalTransactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +@Service +public class ShopUserServiceImpl implements ShopUserService { + + @Resource + private ShopUserMapper shopUserMapper; + @Autowired + private AccountFeignClient accountFeignClient; + @Autowired + private UserRoleFeignClient userRoleFeignClient; + @Autowired + private SegmentFeignClient segmentFeignClient; + + @Override + public PageVO pageByShopId(PageDTO pageDTO, Long shopId, String nickName) { + return PageUtil.doPage(pageDTO, () -> shopUserMapper.listByShopId(shopId, nickName)); + } + + @Override + public ShopUserVO getByUserId(Long userId) { + ShopUserVO shopUser = shopUserMapper.getByUserId(userId); + ServerResponseEntity> roleIds = userRoleFeignClient.getRoleIds(shopUser.getShopUserId()); + shopUser.setRoleIds(roleIds.getData()); + return shopUser; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(ShopUser shopUser, List roleIds) { + shopUserMapper.save(shopUser); + if (CollUtil.isEmpty(roleIds)) { + return; + } + UserRoleDTO userRoleDTO = new UserRoleDTO(); + userRoleDTO.setRoleIds(roleIds); + userRoleDTO.setUserId(shopUser.getShopUserId()); + userRoleFeignClient.saveByUserIdAndSysType(userRoleDTO); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public void update(ShopUser shopUser, List roleIds) { + UserRoleDTO userRoleDTO = new UserRoleDTO(); + userRoleDTO.setRoleIds(roleIds); + userRoleDTO.setUserId(shopUser.getShopUserId()); + shopUserMapper.update(shopUser); + userRoleFeignClient.updateByUserIdAndSysType(userRoleDTO); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public void deleteById(Long shopUserId) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + accountFeignClient.deleteByUserIdAndSysType(shopUserId); + userRoleFeignClient.deleteByUserIdAndSysType(shopUserId); + shopUserMapper.deleteById(shopUserId); + } + + @Override + public Long getUserIdByShopId(Long shopId) { + return shopUserMapper.getUserIdByShopId(shopId); + } + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/HotSearchVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/HotSearchVO.java new file mode 100644 index 00000000..2b63fcb5 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/HotSearchVO.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.multishop.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 热搜VO + * + * @author YXF + * @date 2021-01-27 09:10:00 + */ +public class HotSearchVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long hotSearchId; + + @ApiModelProperty("店铺ID 0为全局热搜") + private Long shopId; + + @ApiModelProperty("内容") + private String content; + + @ApiModelProperty("顺序") + private Integer seq; + + @ApiModelProperty("状态 0下线 1上线") + private Integer status; + + @ApiModelProperty("热搜标题") + private String title; + + public Long getHotSearchId() { + return hotSearchId; + } + + public void setHotSearchId(Long hotSearchId) { + this.hotSearchId = hotSearchId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return "HotSearchVO{" + + "hotSearchId=" + hotSearchId + + ",shopId=" + shopId + + ",content=" + content + + ",seq=" + seq + + ",status=" + status + + ",title=" + title + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/IndexImgVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/IndexImgVO.java new file mode 100644 index 00000000..fde761cb --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/IndexImgVO.java @@ -0,0 +1,122 @@ +package com.mall4j.cloud.multishop.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 轮播图VO + * + * @author YXF + * @date 2020-11-24 16:38:32 + */ +public class IndexImgVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long imgId; + + @ApiModelProperty("店铺ID") + private Long shopId; + + @ApiModelProperty("图片") + @JsonSerialize(using = ImgJsonSerializer.class) + private String imgUrl; + + @ApiModelProperty("状态") + private Integer status; + + @ApiModelProperty("顺序") + private Integer seq; + + @ApiModelProperty("关联商品id") + private Long spuId; + + @ApiModelProperty("图片类型 0:小程序 1:pc") + private Integer imgType; + + @ApiModelProperty("spu信息") + private SpuVO spu; + + public Long getImgId() { + return imgId; + } + + public void setImgId(Long imgId) { + this.imgId = imgId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getImgType() { + return imgType; + } + + public void setImgType(Integer imgType) { + this.imgType = imgType; + } + + public SpuVO getSpu() { + return spu; + } + + public void setSpu(SpuVO spu) { + this.spu = spu; + } + + @Override + public String toString() { + return "IndexImgVO{" + + "imgId=" + imgId + + ",shopId=" + shopId + + ",imgUrl=" + imgUrl + + ",status=" + status + + ",seq=" + seq + + ",spuId=" + spuId + + ",imgType=" + imgType + + ",spu=" + spu + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopDetailAppVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopDetailAppVO.java new file mode 100644 index 00000000..ff5c27b0 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopDetailAppVO.java @@ -0,0 +1,149 @@ +package com.mall4j.cloud.multishop.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.api.vo.search.SpuSearchVO; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 店铺详情VO + * + * @author FrozenWatermelon + * @date 2020-12-05 15:50:25 + */ +public class ShopDetailAppVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("店铺简介") + private String intro; + + @ApiModelProperty("店铺logo(可修改)") + @JsonSerialize(using = ImgJsonSerializer.class) + private String shopLogo; + + @ApiModelProperty("店铺状态(-1:未开通 0: 停业中 1:营业中)") + private Integer shopStatus; + + @ApiModelProperty("营业执照") + @JsonSerialize(using = ImgJsonSerializer.class) + private String businessLicense; + + @ApiModelProperty("身份证正面") + @JsonSerialize(using = ImgJsonSerializer.class) + private String identityCardFront; + + @ApiModelProperty("身份证反面") + @JsonSerialize(using = ImgJsonSerializer.class) + private String identityCardLater; + + @ApiModelProperty("商品列表") + private List spuList; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getIntro() { + return intro; + } + + public void setIntro(String intro) { + this.intro = intro; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + public String getBusinessLicense() { + return businessLicense; + } + + public void setBusinessLicense(String businessLicense) { + this.businessLicense = businessLicense; + } + + public String getIdentityCardFront() { + return identityCardFront; + } + + public void setIdentityCardFront(String identityCardFront) { + this.identityCardFront = identityCardFront; + } + + public String getIdentityCardLater() { + return identityCardLater; + } + + public void setIdentityCardLater(String identityCardLater) { + this.identityCardLater = identityCardLater; + } + + public List getSpuList() { + return spuList; + } + + public void setSpuList(List spuList) { + this.spuList = spuList; + } + + @Override + public String toString() { + return "ShopDetailAppVO{" + + "shopId=" + shopId + + ", type=" + type + + ", shopName='" + shopName + '\'' + + ", intro='" + intro + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", shopStatus=" + shopStatus + + ", businessLicense='" + businessLicense + '\'' + + ", identityCardFront='" + identityCardFront + '\'' + + ", identityCardLater='" + identityCardLater + '\'' + + ", spuList=" + spuList + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopHeadInfoVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopHeadInfoVO.java new file mode 100644 index 00000000..e2db18ec --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopHeadInfoVO.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved. + * + * https://www.gz-yami.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.mall4j.cloud.multishop.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +/** + * 店铺的头信息 + * @author LGH + */ +public class ShopHeadInfoVO { + + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺类型1自营店 2普通店") + private Integer type; + + @ApiModelProperty("店长用户id") + private Long ownerUserId; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty("店铺简介") + private String intro; + + @ApiModelProperty("手机号码") + private String noticeMobile; + + @ApiModelProperty("店铺logo(可修改)") + @JsonSerialize(using = ImgJsonSerializer.class) + private String shopLogo; + + @ApiModelProperty("是否优选好店 1.是 0.不是") + private Integer isPreferred; + + @ApiModelProperty("店铺状态(-1:未开通 0: 停业中 1:营业中 2:平台下线 3:平台下线待审核)") + private Integer shopStatus; + + @ApiModelProperty("移动端背景图") + @JsonSerialize(using = ImgJsonSerializer.class) + private String mobileBackgroundPic; + + @ApiModelProperty("pc背景图") + @JsonSerialize(using = ImgJsonSerializer.class) + private String pcBackgroundPic; + + public String getMobileBackgroundPic() { + return mobileBackgroundPic; + } + + public void setMobileBackgroundPic(String mobileBackgroundPic) { + this.mobileBackgroundPic = mobileBackgroundPic; + } + + public String getPcBackgroundPic() { + return pcBackgroundPic; + } + + public void setPcBackgroundPic(String pcBackgroundPic) { + this.pcBackgroundPic = pcBackgroundPic; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Long getOwnerUserId() { + return ownerUserId; + } + + public void setOwnerUserId(Long ownerUserId) { + this.ownerUserId = ownerUserId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getIntro() { + return intro; + } + + public void setIntro(String intro) { + this.intro = intro; + } + + public String getNoticeMobile() { + return noticeMobile; + } + + public void setNoticeMobile(String noticeMobile) { + this.noticeMobile = noticeMobile; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getIsPreferred() { + return isPreferred; + } + + public void setIsPreferred(Integer isPreferred) { + this.isPreferred = isPreferred; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + @Override + public String toString() { + return "ShopHeadInfoVO{" + + "shopId=" + shopId + + ", type=" + type + + ", ownerUserId=" + ownerUserId + + ", shopName='" + shopName + '\'' + + ", intro='" + intro + '\'' + + ", noticeMobile='" + noticeMobile + '\'' + + ", shopLogo='" + shopLogo + '\'' + + ", isPreferred=" + isPreferred + + ", shopStatus=" + shopStatus + + ", mobileBackgroundPic='" + mobileBackgroundPic + '\'' + + ", pcBackgroundPic='" + pcBackgroundPic + '\'' + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserSimpleVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserSimpleVO.java new file mode 100644 index 00000000..939f8e55 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserSimpleVO.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.multishop.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/9/2 + */ +public class ShopUserSimpleVO { + + /** + * 昵称 + */ + @ApiModelProperty("昵称") + private String nickName; + + /** + * 头像 + */ + @ApiModelProperty("头像") + @JsonSerialize(using = ImgJsonSerializer.class) + private String avatar; + + private Integer isAdmin; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "ShopUserSimpleVO{" + + "nickName='" + nickName + '\'' + + ", avatar='" + avatar + '\'' + + ", isAdmin=" + isAdmin + + '}'; + } + +} diff --git a/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserVO.java b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserVO.java new file mode 100644 index 00000000..d62e3912 --- /dev/null +++ b/mall4cloud-multishop/src/main/java/com/mall4j/cloud/multishop/vo/ShopUserVO.java @@ -0,0 +1,114 @@ +package com.mall4j.cloud.multishop.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/9/2 + */ +public class ShopUserVO { + + /** + * shopUserId + */ + @ApiModelProperty("店铺用户id") + private Long shopUserId; + + /** + * 昵称 + */ + @ApiModelProperty("昵称") + private String nickName; + + /** + * 员工编号 + */ + @ApiModelProperty("员工编号") + private String code; + + /** + * 联系方式 + */ + @ApiModelProperty("联系方式") + private String phoneNum; + + @ApiModelProperty("是否已经有账号了") + private Integer hasAccount; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("角色id列表") + private List roleIds; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Long getShopUserId() { + return shopUserId; + } + + public void setShopUserId(Long shopUserId) { + this.shopUserId = shopUserId; + } + + public Integer getHasAccount() { + return hasAccount; + } + + public void setHasAccount(Integer hasAccount) { + this.hasAccount = hasAccount; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + @Override + public String toString() { + return "ShopUserVO{" + + "shopUserId=" + shopUserId + + ", nickName='" + nickName + '\'' + + ", code='" + code + '\'' + + ", phoneNum='" + phoneNum + '\'' + + ", hasAccount=" + hasAccount + + ", shopId=" + shopId + + ", roleIds=" + roleIds + + '}'; + } +} diff --git a/mall4cloud-multishop/src/main/resources/bootstrap.yml b/mall4cloud-multishop/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..d637fb57 --- /dev/null +++ b/mall4cloud-multishop/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 9103 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ + diff --git a/mall4cloud-multishop/src/main/resources/mapper/HotSearchMapper.xml b/mall4cloud-multishop/src/main/resources/mapper/HotSearchMapper.xml new file mode 100644 index 00000000..2e329b41 --- /dev/null +++ b/mall4cloud-multishop/src/main/resources/mapper/HotSearchMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + `hot_search_id`,`shop_id`,`content`,`create_time`,`update_time`,`seq`,`status`,`title` + + + + + insert into hot_search (`shop_id`,`content`,`seq`,`status`,`title`) + values (#{hotSearch.shopId},#{hotSearch.content},#{hotSearch.seq},#{hotSearch.status},#{hotSearch.title}); + + + update hot_search + set `content` = #{hotSearch.content},`seq` = #{hotSearch.seq},`status` = #{hotSearch.status},`title` = #{hotSearch.title} + where hot_search_id = #{hotSearch.hotSearchId} and shop_id = #{hotSearch.shopId} + + + delete from hot_search where hot_search_id = #{hotSearchId} and shop_id = #{shopId} + + + + diff --git a/mall4cloud-multishop/src/main/resources/mapper/IndexImgMapper.xml b/mall4cloud-multishop/src/main/resources/mapper/IndexImgMapper.xml new file mode 100644 index 00000000..bb679d58 --- /dev/null +++ b/mall4cloud-multishop/src/main/resources/mapper/IndexImgMapper.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + `img_id`,`shop_id`,`img_url`,`status`,`seq`,`spu_id`,`img_type`,`create_time`,`update_time` + + + + + insert into index_img (`shop_id`,`img_url`,`status`,`seq`,`spu_id`,`img_type`) + values (#{indexImg.shopId},#{indexImg.imgUrl},#{indexImg.status},#{indexImg.seq},#{indexImg.spuId},#{indexImg.imgType}); + + + update index_img + set `spu_id` = #{indexImg.spuId}, `img_url` = #{indexImg.imgUrl}, `status` = #{indexImg.status},`seq` = #{indexImg.seq},`img_type` = #{indexImg.imgType} + where img_id = #{indexImg.imgId} and shop_id = #{indexImg.shopId} + + + update index_img + set `spu_id` = null + where `spu_id` = #{spuId} + + + delete from index_img where img_id = #{imgId} and shop_id = #{shopId} + + + + diff --git a/mall4cloud-multishop/src/main/resources/mapper/ShopDetailMapper.xml b/mall4cloud-multishop/src/main/resources/mapper/ShopDetailMapper.xml new file mode 100644 index 00000000..28bc9881 --- /dev/null +++ b/mall4cloud-multishop/src/main/resources/mapper/ShopDetailMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + `shop_id`,`create_time`,`update_time`,`type`,`shop_name`,`intro`,`shop_logo`,`mobile_background_pic`,`shop_status`,`business_license`,`identity_card_front`,`identity_card_later` + + + + + + insert into shop_detail (`type`,`shop_name`,`intro`,`shop_logo`,`mobile_background_pic`,`shop_status`,`business_license`,`identity_card_front`,`identity_card_later`) + values (#{shopDetail.type},#{shopDetail.shopName},#{shopDetail.intro},#{shopDetail.shopLogo},#{shopDetail.mobileBackgroundPic},#{shopDetail.shopStatus},#{shopDetail.businessLicense},#{shopDetail.identityCardFront},#{shopDetail.identityCardLater}); + + + update shop_detail + + + `type` = #{shopDetail.type}, + + + `shop_name` = #{shopDetail.shopName}, + + + `intro` = #{shopDetail.intro}, + + + `shop_logo` = #{shopDetail.shopLogo}, + + + `mobile_background_pic` = #{shopDetail.mobileBackgroundPic} + + + `shop_status` = #{shopDetail.shopStatus}, + + + `business_license` = #{shopDetail.businessLicense}, + + + `identity_card_front` = #{shopDetail.identityCardFront}, + + + `identity_card_later` = #{shopDetail.identityCardLater}, + + + where shop_id = #{shopDetail.shopId} + + + delete from shop_detail where shop_id = #{shopId} + + + + + + + + update shop_detail set `shop_status` = #{shopStatus} where shop_id = #{shopId} + + + + + + + diff --git a/mall4cloud-multishop/src/main/resources/mapper/ShopUserMapper.xml b/mall4cloud-multishop/src/main/resources/mapper/ShopUserMapper.xml new file mode 100644 index 00000000..2a37cbf2 --- /dev/null +++ b/mall4cloud-multishop/src/main/resources/mapper/ShopUserMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + shop_user_id, create_time, update_time, shop_id, nick_name,`code`,phone_num,has_account + + + + + + insert into `shop_user` ( `shop_user_id`, `shop_id`, `nick_name`, `code`, `phone_num`,has_account) + values (#{shopUser.shopUserId},#{shopUser.shopId},#{shopUser.nickName},#{shopUser.code},#{shopUser.phoneNum}, #{shopUser.hasAccount}); + + + update shop_user + + + nick_name = #{shopUser.nickName}, + + + code = #{shopUser.code}, + + + phone_num = #{shopUser.phoneNum}, + + + has_account = #{shopUser.hasAccount}, + + + where shop_user_id = #{shopUser.shopUserId} and shop_id = #{shopUser.shopId} + + + + delete from shop_user where shop_user_id = #{shopUserId} + + + + diff --git a/mall4cloud-order/pom.xml b/mall4cloud-order/pom.xml new file mode 100644 index 00000000..249a6765 --- /dev/null +++ b/mall4cloud-order/pom.xml @@ -0,0 +1,90 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-order + mall4cloud 订单服务 + jar + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-rocketmq + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-user + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-product + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-search + ${project.version} + + + org.springframework.cloud + spring-cloud-commons + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/OrderApplication.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/OrderApplication.java new file mode 100644 index 00000000..866fdd2e --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/OrderApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.order; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/11/19 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class OrderApplication { + + public static void main(String[] args) { + SpringApplication.run(OrderApplication.class, args); + } + +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/bo/SubmitOrderPayAmountInfoBO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/bo/SubmitOrderPayAmountInfoBO.java new file mode 100644 index 00000000..da983701 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/bo/SubmitOrderPayAmountInfoBO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.order.bo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * @author FrozenWatermelon + * @date 2021/2/4 + */ +public class SubmitOrderPayAmountInfoBO { + + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "总共需要支付金额") + private Long totalFee; + + @ApiModelProperty(value = "订单地址id") + private Long orderAddrId; + + public Long getTotalFee() { + return totalFee; + } + + public void setTotalFee(Long totalFee) { + this.totalFee = totalFee; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + @Override + public String toString() { + return "SubmitOrderPayAmountInfoBO{" + + "createTime=" + createTime + + ", totalFee=" + totalFee + + ", orderAddrId=" + orderAddrId + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/OrderCacheTtlAdapter.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/OrderCacheTtlAdapter.java new file mode 100644 index 00000000..1774a10d --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/OrderCacheTtlAdapter.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.order.config; + +import com.mall4j.cloud.common.cache.adapter.CacheTtlAdapter; +import com.mall4j.cloud.common.cache.bo.CacheNameWithTtlBO; +import com.mall4j.cloud.common.cache.constant.OrderCacheNames; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/12/18 + */ +@Component +public class OrderCacheTtlAdapter implements CacheTtlAdapter { + @Override + public List listCacheNameWithTtl() { + List cacheNameWithTtls = new ArrayList<>(); + // 确认订单缓存30分钟 + cacheNameWithTtls.add(new CacheNameWithTtlBO(OrderCacheNames.ORDER_CONFIRM_UUID_KEY, 60 * 30)); + cacheNameWithTtls.add(new CacheNameWithTtlBO(OrderCacheNames.ORDER_CONFIRM_KEY, 60 * 30)); + return cacheNameWithTtls; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/RocketMqConfig.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/RocketMqConfig.java new file mode 100644 index 00000000..d45dcd7e --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/RocketMqConfig.java @@ -0,0 +1,41 @@ +package com.mall4j.cloud.order.config; + +import com.mall4j.cloud.common.rocketmq.config.RocketMqAdapter; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author FrozenWatermelon + * @date 2021/3/30 + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate stockMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.STOCK_UNLOCK_TOPIC); + } + + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderCancelTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.ORDER_CANCEL_TOPIC); + } + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderNotifyStockTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/SwaggerConfiguration.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/SwaggerConfiguration.java new file mode 100644 index 00000000..a452b6c5 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.order.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.order.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/MyOrderController.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/MyOrderController.java new file mode 100644 index 00000000..d7c41cd9 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/MyOrderController.java @@ -0,0 +1,164 @@ +package com.mall4j.cloud.order.controller.app; + +import com.mall4j.cloud.api.feign.SearchOrderFeignClient; +import com.mall4j.cloud.api.order.constant.OrderStatus; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.EsOrderVO; +import com.mall4j.cloud.common.dto.OrderSearchDTO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.order.model.Order; +import com.mall4j.cloud.order.model.OrderAddr; +import com.mall4j.cloud.order.model.OrderItem; +import com.mall4j.cloud.order.service.*; +import com.mall4j.cloud.order.service.OrderAddrService; +import com.mall4j.cloud.order.service.OrderItemService; +import com.mall4j.cloud.order.service.OrderService; +import com.mall4j.cloud.order.vo.OrderAddrVO; +import com.mall4j.cloud.order.vo.OrderCountVO; +import com.mall4j.cloud.order.vo.OrderItemVO; +import com.mall4j.cloud.order.vo.OrderShopVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * 我的订单 + * + * @author FrozenWatermelon + */ +@RestController +@RequestMapping("/p/myOrder") +@Api(tags = "app-我的订单接口") +public class MyOrderController { + + @Autowired + private OrderService orderService; + @Autowired + private MapperFacade mapperFacade; + @Autowired + private OrderItemService orderItemService; + @Autowired + private SearchOrderFeignClient searchOrderFeignClient; + @Autowired + private OrderAddrService orderAddrService; + + + /** + * 订单详情信息接口 + */ + @GetMapping("/order_detail") + @ApiOperation(value = "订单详情信息", notes = "根据订单号获取订单详情信息") + @ApiImplicitParam(name = "orderId", value = "订单号", required = true, dataType = "Long") + public ServerResponseEntity orderDetail(@RequestParam(value = "orderId") Long orderId) { + Long userId = AuthUserContext.get().getUserId(); + OrderShopVO orderShopDto = new OrderShopVO(); + Order order = orderService.getOrderByOrderIdAndUserId(orderId, userId); + OrderAddr orderAddr = orderAddrService.getByOrderAddrId(order.getOrderAddrId()); + List orderItems = orderItemService.listOrderItemsByOrderId(orderId); + orderShopDto.setShopId(order.getShopId()); + orderShopDto.setDeliveryType(order.getDeliveryType()); + orderShopDto.setShopName(order.getShopName()); + orderShopDto.setCreateTime(order.getCreateTime()); + orderShopDto.setStatus(order.getStatus()); + orderShopDto.setOrderAddr(mapperFacade.map(orderAddr, OrderAddrVO.class)); + // 付款时间 + orderShopDto.setPayTime(order.getPayTime()); + // 发货时间 + orderShopDto.setDeliveryTime(order.getDeliveryTime()); + // 完成时间 + orderShopDto.setFinallyTime(order.getFinallyTime()); + // 取消时间 + orderShopDto.setCancelTime(order.getCancelTime()); + // 更新时间 + orderShopDto.setUpdateTime(order.getUpdateTime()); + orderShopDto.setOrderItems(mapperFacade.mapAsList(orderItems, OrderItemVO.class)); + orderShopDto.setTotal(order.getTotal()); + orderShopDto.setTotalNum(order.getAllCount()); + + return ServerResponseEntity.success(orderShopDto); + } + + @GetMapping("/order_count") + @ApiOperation(value = "计算各个订单数量", notes = "根据订单状态计算各个订单数量") + public ServerResponseEntity orderCount() { + Long userId = AuthUserContext.get().getUserId(); + OrderCountVO orderCount = orderService.countNumberOfStatus(userId); + return ServerResponseEntity.success(orderCount); + } + + /** + * 分页获取 + */ + @GetMapping("/search_order") + @ApiOperation(value = "订单列表信息查询", notes = "根据订单编号或者订单中商品名称搜索") + public ServerResponseEntity> searchOrder(OrderSearchDTO orderSearchDTO) { + Long userId = AuthUserContext.get().getUserId(); + orderSearchDTO.setUserId(userId); + return searchOrderFeignClient.getOrderPage(orderSearchDTO); + } + + /** + * 取消订单 + */ + @PutMapping("/cancel/{orderId}") + @ApiOperation(value = "根据订单号取消订单", notes = "根据订单号取消订单") + @ApiImplicitParam(name = "orderId", value = "订单号", required = true, dataType = "String") + public ServerResponseEntity cancel(@PathVariable("orderId") Long orderId) { + Long userId = AuthUserContext.get().getUserId(); + Order order = orderService.getOrderByOrderIdAndUserId(orderId, userId); + if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) { + // 订单已支付,无法取消订单 + return ServerResponseEntity.fail(ResponseEnum.ORDER_PAYED); + } + // 如果订单未支付的话,将订单设为取消状态 + orderService.cancelOrderAndGetCancelOrderIds(Collections.singletonList(order.getOrderId())); + return ServerResponseEntity.success(); + } + + + /** + * 确认收货 + */ + @PutMapping("/receipt/{orderId}") + @ApiOperation(value = "根据订单号确认收货", notes = "根据订单号确认收货") + public ServerResponseEntity receipt(@PathVariable("orderId") Long orderId) { + Long userId = AuthUserContext.get().getUserId(); + Order order = orderService.getOrderByOrderIdAndUserId(orderId, userId); + if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) { + // 订单未发货,无法确认收货 + return ServerResponseEntity.fail(ResponseEnum.ORDER_NO_DELIVERY); + } + List orderItems = orderItemService.listOrderItemsByOrderId(orderId); + order.setOrderItems(orderItems); + // 确认收货 + orderService.receiptOrder(order.getOrderId()); + return ServerResponseEntity.success(); + } + + /** + * 删除订单 + */ + @DeleteMapping("/{orderId}") + @ApiOperation(value = "根据订单号删除订单", notes = "根据订单号删除订单") + @ApiImplicitParam(name = "orderId", value = "订单号", required = true, dataType = "String") + public ServerResponseEntity delete(@PathVariable("orderId") Long orderId) { + Long userId = AuthUserContext.get().getUserId(); + Order order = orderService.getOrderByOrderIdAndUserId(orderId, userId); + if (!Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value()) && !Objects.equals(order.getStatus(), OrderStatus.CLOSE.value()) ) { + // 订单未完成或未关闭,无法删除订单 + return ServerResponseEntity.fail(ResponseEnum.ORDER_NOT_FINISH_OR_CLOSE); + } + // 删除订单 + orderService.deleteOrder(order.getOrderId()); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/OrderController.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/OrderController.java new file mode 100644 index 00000000..c0dd49fe --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/app/OrderController.java @@ -0,0 +1,172 @@ +package com.mall4j.cloud.order.controller.app; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.product.manager.ShopCartAdapter; +import com.mall4j.cloud.api.user.feign.UserAddrFeignClient; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.constant.OrderCacheNames; +import com.mall4j.cloud.common.cache.util.CacheManagerUtil; +import com.mall4j.cloud.common.cache.util.RedisUtil; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.order.vo.*; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.order.bo.SubmitOrderPayAmountInfoBO; +import com.mall4j.cloud.order.dto.app.OrderDTO; +import com.mall4j.cloud.order.model.OrderAddr; +import com.mall4j.cloud.order.service.OrderAddrService; +import com.mall4j.cloud.order.service.OrderItemService; +import com.mall4j.cloud.order.service.OrderService; +import com.mall4j.cloud.order.vo.SubmitOrderPayInfoVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.*; + +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +@RestController("appOrderController") +@RequestMapping("/a/order") +@Api(tags = "app-订单信息") +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private ShopCartAdapter shopCartAdapter; + + @Autowired + private CacheManagerUtil cacheManagerUtil; + + @Autowired + private OrderItemService orderItemService; + + @Autowired + private OrderAddrService orderAddrService; + + @Autowired + private UserAddrFeignClient userAddrFeignClient; + + /** + * 生成订单 + */ + @PostMapping("/confirm") + @ApiOperation(value = "结算,生成订单信息", notes = "传入下单所需要的参数进行下单") + public ServerResponseEntity confirm(@Valid @RequestBody OrderDTO orderParam){ + Long userId = AuthUserContext.get().getUserId(); + // 将要返回给前端的完整的订单信息 + ShopCartOrderMergerVO shopCartOrderMerger = new ShopCartOrderMergerVO(); + shopCartOrderMerger.setDvyType(orderParam.getDvyType()); + ServerResponseEntity addrFeign = userAddrFeignClient.getUserAddrByAddrId(orderParam.getAddrId()); + if (addrFeign.isSuccess()){ + shopCartOrderMerger.setUserAddr(addrFeign.getData()); + } + ServerResponseEntity> shopCartItemResponse = shopCartAdapter.getShopCartItems(orderParam.getShopCartItem()); + if (!shopCartItemResponse.isSuccess()) { + return ServerResponseEntity.transform(shopCartItemResponse); + } + List shopCartItems = shopCartItemResponse.getData(); + // 购物车 + List shopCarts = shopCartAdapter.conversionShopCart(shopCartItems); + // 重算一遍订单金额 + recalculateAmountWhenFinishingCalculateShop(shopCartOrderMerger, shopCarts); + // 防止重复提交 + RedisUtil.STRING_REDIS_TEMPLATE.opsForValue().set(OrderCacheNames.ORDER_CONFIRM_UUID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); + // 保存订单计算结果缓存,省得重新计算并且用户确认的订单金额与提交的一致 + cacheManagerUtil.putCache(OrderCacheNames.ORDER_CONFIRM_KEY,String.valueOf(userId),shopCartOrderMerger); + return ServerResponseEntity.success(shopCartOrderMerger); + } + + /** + * 这里有提交订单的代码 + * 购物车/立即购买 提交订单,根据店铺拆单 + */ + @PostMapping("/submit") + @ApiOperation(value = "提交订单,返回支付流水号", notes = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付") + public ServerResponseEntity> submitOrders() { + Long userId = AuthUserContext.get().getUserId(); + ShopCartOrderMergerVO mergerOrder = cacheManagerUtil.getCache(OrderCacheNames.ORDER_CONFIRM_KEY, String.valueOf(userId)); + // 看看订单有没有过期 + if (mergerOrder == null) { + return ServerResponseEntity.fail(ResponseEnum.ORDER_EXPIRED); + } + // 防止重复提交 + boolean cad = RedisUtil.cad(OrderCacheNames.ORDER_CONFIRM_UUID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); + if (!cad) { + return ServerResponseEntity.fail(ResponseEnum.REPEAT_ORDER); + } + List orderIds = orderService.submit(userId,mergerOrder); + return ServerResponseEntity.success(orderIds); + } + + + @GetMapping("/order_pay_info") + @ApiOperation(value = "获取订单支付信息", notes = "获取订单支付的商品/地址信息") + @ApiImplicitParam(name = "orderIds", value = "订单流水号", required = true, dataType = "String") + public ServerResponseEntity getOrderPayInfoByOrderNumber(@RequestParam("orderIds") String orderIds) { + long[] orderIdList = StrUtil.splitToLong(orderIds, ","); + List spuNameList = orderItemService.getSpuNameListByOrderIds(orderIdList); + //获取订单信息 + SubmitOrderPayAmountInfoBO submitOrderPayAmountInfo = orderService.getSubmitOrderPayAmountInfo(orderIdList); + if (Objects.isNull(submitOrderPayAmountInfo) || Objects.isNull(submitOrderPayAmountInfo.getCreateTime()) ) { + return ServerResponseEntity.fail(ResponseEnum.ORDER_NOT_EXIST); + } + Date endTime = DateUtil.offsetMinute(submitOrderPayAmountInfo.getCreateTime(), Constant.ORDER_CANCEL_TIME); + SubmitOrderPayInfoVO orderPayInfoParam = new SubmitOrderPayInfoVO(); + orderPayInfoParam.setSpuNameList(spuNameList); + orderPayInfoParam.setEndTime(endTime); + orderPayInfoParam.setTotalFee(submitOrderPayAmountInfo.getTotalFee()); + // 地址 + if (Objects.nonNull(submitOrderPayAmountInfo.getOrderAddrId())) { + OrderAddr orderAddr = orderAddrService.getByOrderAddrId(submitOrderPayAmountInfo.getOrderAddrId()); + //写入商品名、收货地址/电话 + String addr = orderAddr.getProvince() + orderAddr.getCity() + orderAddr.getArea() + orderAddr.getAddr(); + orderPayInfoParam.setUserAddr(addr); + orderPayInfoParam.setConsignee(orderAddr.getConsignee()); + orderPayInfoParam.setMobile(orderAddr.getMobile()); + } + return ServerResponseEntity.success(orderPayInfoParam); + } + + /** + * 重算一遍订单金额 + */ + private void recalculateAmountWhenFinishingCalculateShop(ShopCartOrderMergerVO shopCartOrderMerger, List shopCarts) { + // 所有店铺的订单信息 + List shopCartOrders = new ArrayList<>(); + long total = 0; + int totalCount = 0; + // 所有店铺所有的商品item + for (ShopCartVO shopCart : shopCarts) { + // 每个店铺的订单信息 + ShopCartOrderVO shopCartOrder = new ShopCartOrderVO(); + shopCartOrder.setShopId(shopCart.getShopId()); + shopCartOrder.setShopName(shopCart.getShopName()); + total += shopCart.getTotal(); + totalCount += shopCart.getTotalCount(); + shopCartOrder.setTotal(shopCart.getTotal()); + shopCartOrder.setTotalCount(shopCart.getTotalCount()); + shopCartOrder.setShopCartItemVO(shopCart.getshopCartItem()); + shopCartOrders.add(shopCartOrder); + } + shopCartOrderMerger.setTotal(total); + shopCartOrderMerger.setTotalCount(totalCount); + shopCartOrderMerger.setShopCartOrders(shopCartOrders); + } + + public static void main(String[] args) { + System.out.println(new BCryptPasswordEncoder().encode("123456")); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/multishop/OrderController.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/multishop/OrderController.java new file mode 100644 index 00000000..8c95a474 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/controller/multishop/OrderController.java @@ -0,0 +1,113 @@ +package com.mall4j.cloud.order.controller.multishop; + +import com.mall4j.cloud.api.feign.SearchOrderFeignClient; +import com.mall4j.cloud.api.order.constant.OrderStatus; +import com.mall4j.cloud.api.order.dto.DeliveryOrderDTO; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.EsOrderVO; +import com.mall4j.cloud.common.dto.OrderSearchDTO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.order.model.Order; +import com.mall4j.cloud.order.model.OrderAddr; +import com.mall4j.cloud.order.service.OrderAddrService; +import com.mall4j.cloud.order.service.OrderService; +import com.mall4j.cloud.order.vo.OrderAddrVO; +import com.mall4j.cloud.order.vo.OrderVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * @author FrozenWatermelon on 2018/09/15. + */ +@RestController("multishopOrderController") +@Controller +@RequestMapping("/m/order") +@Api(tags = "multishop-订单接口") +public class OrderController { + + @Autowired + private OrderService orderService; + + @Autowired + private MapperFacade mapperFacade; + + @Autowired + private SearchOrderFeignClient searchOrderFeignClient; + + @Autowired + private OrderAddrService orderAddrService; + /** + * 分页获取 + */ + @GetMapping("/page") + @ApiOperation(value = "分页获取订单详情") + public ServerResponseEntity> page(OrderSearchDTO orderSearchDTO) { + Long shopId = AuthUserContext.get().getTenantId(); + orderSearchDTO.setShopId(shopId); + return searchOrderFeignClient.getOrderPage(orderSearchDTO); + } + + /** + * 获取信息 + */ + @GetMapping("/order_info/{orderId}") + @ApiOperation(value = "根据id获取订单详情") + public ServerResponseEntity info(@PathVariable("orderId") Long orderId) { + // 订单和订单项 + Order order = orderService.getOrderAndOrderItemData(orderId, AuthUserContext.get().getTenantId()); + // 详情用户收货地址 + OrderAddr orderAddr = orderAddrService.getByOrderAddrId(order.getOrderAddrId()); + order.setOrderAddr(mapperFacade.map(orderAddr, OrderAddr.class)); + OrderVO orderVO = mapperFacade.map(order, OrderVO.class); + return ServerResponseEntity.success(orderVO); + } + /** + * 获取订单用户下单地址 + */ + @GetMapping("/order_addr/{orderAddrId}") + @ApiOperation(value = "获取订单用户下单地址") + public ServerResponseEntity getOrderAddr(@PathVariable("orderAddrId") Long orderAddrId) { + OrderAddr orderAddr = orderAddrService.getByOrderAddrId(orderAddrId); + return ServerResponseEntity.success(mapperFacade.map(orderAddr, OrderAddrVO.class)); + } + + /** + * 订单项待发货数量查询 + */ + @GetMapping("/order_item_and_address/{orderId}") + @ApiOperation(value = "订单项待发货数量查询") + public ServerResponseEntity getOrderItemAndAddress(@PathVariable("orderId") Long orderId) { + // 订单和订单项 + Order order = orderService.getOrderAndOrderItemData(orderId, AuthUserContext.get().getTenantId()); + OrderVO orderVO = mapperFacade.map(order, OrderVO.class); + // 用户收货地址 + OrderAddr orderAddr = orderAddrService.getByOrderAddrId(order.getOrderAddrId()); + orderVO.setOrderAddr(mapperFacade.map(orderAddr, OrderAddrVO.class)); + return ServerResponseEntity.success(orderVO); + } + + /** + * 发货 + */ + @PostMapping("/delivery") + @ApiOperation(value = "发货") + public ServerResponseEntity delivery(@Valid @RequestBody DeliveryOrderDTO deliveryOrderParam) { + OrderVO order = orderService.getOrderByOrderId(deliveryOrderParam.getOrderId()); + // 订单不在支付状态 + if (!Objects.equals(order.getStatus(), OrderStatus.PADYED.value())){ + return ServerResponseEntity.fail(ResponseEnum.ORDER_NOT_PAYED); + } + orderService.delivery(deliveryOrderParam); + return ServerResponseEntity.success(); + } + +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/app/OrderDTO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/app/OrderDTO.java new file mode 100644 index 00000000..700891cf --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/app/OrderDTO.java @@ -0,0 +1,59 @@ +package com.mall4j.cloud.order.dto.app; + +import com.mall4j.cloud.api.product.dto.ShopCartItemDTO; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +/** + * 订单参数 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderDTO { + + @ApiModelProperty(value = "立即购买时提交的商品项,如果该值为空,则说明是从购物车进入,如果该值不为空则说明为立即购买") + private ShopCartItemDTO shopCartItem; + + @NotNull(message = "配送类型不能为空") + @ApiModelProperty(value = "配送类型3:无需快递") + private Integer dvyType; + + @ApiModelProperty(value = "地址ID,0为默认地址") + @NotNull(message = "地址不能为空") + private Long addrId; + + public Integer getDvyType() { + return dvyType; + } + + public void setDvyType(Integer dvyType) { + this.dvyType = dvyType; + } + + public ShopCartItemDTO getShopCartItem() { + return shopCartItem; + } + + public void setShopCartItem(ShopCartItemDTO shopCartItem) { + this.shopCartItem = shopCartItem; + } + + public Long getAddrId() { + return addrId; + } + + public void setAddrId(Long addrId) { + this.addrId = addrId; + } + + @Override + public String toString() { + return "OrderDTO{" + + "shopCartItem=" + shopCartItem + + ", dvyType=" + dvyType + + ", addrId=" + addrId + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderAdminDTO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderAdminDTO.java new file mode 100644 index 00000000..abd9fcda --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderAdminDTO.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.order.dto.multishop; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +/** + * 订单参数 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderAdminDTO { + + @ApiModelProperty(value = "订单id") + private Long orderId; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @NotNull(message = "配送类型不能为空") + @ApiModelProperty(value = "配送类型 3:无需快递") + private Integer dvyType; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getDvyType() { + return dvyType; + } + + public void setDvyType(Integer dvyType) { + this.dvyType = dvyType; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + @Override + public String toString() { + return "OrderDTO{" + + ", orderId=" + orderId + + ", shopId=" + shopId + + ", dvyType=" + dvyType + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderItemDTO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderItemDTO.java new file mode 100644 index 00000000..b73570fe --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/dto/multishop/OrderItemDTO.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.order.dto.multishop; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 订单项VO + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderItemDTO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("订单项ID") + private Long orderItemId; + + @ApiModelProperty(value = "变化金额", required = true) + private Long changeAmount; + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getChangeAmount() { + return changeAmount; + } + + public void setChangeAmount(Long changeAmount) { + this.changeAmount = changeAmount; + } + + @Override + public String toString() { + return "OrderItemDTO{" + + "orderItemId=" + orderItemId + + ", changeAmount=" + changeAmount + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/feign/OrderFeignController.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/feign/OrderFeignController.java new file mode 100644 index 00000000..d72c982d --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/feign/OrderFeignController.java @@ -0,0 +1,69 @@ +package com.mall4j.cloud.order.feign; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.bo.OrderSimpleAmountInfoBO; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.constant.OrderStatus; +import com.mall4j.cloud.api.order.feign.OrderFeignClient; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.order.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/12/25 + */ +@RestController +public class OrderFeignController implements OrderFeignClient { + + + @Autowired + private OrderService orderService; + + @Override + public ServerResponseEntity getOrdersAmountAndIfNoCancel(List orderIds) { + List orderStatus = orderService.getOrdersStatus(orderIds); + if (CollectionUtil.isEmpty(orderStatus)) { + return ServerResponseEntity.fail(ResponseEnum.ORDER_NOT_EXIST); + } + + for (OrderStatusBO statusBO : orderStatus) { + // 订单已关闭 + if (statusBO.getStatus() == null || Objects.equals(statusBO.getStatus(), OrderStatus.CLOSE.value())) { + return ServerResponseEntity.fail(ResponseEnum.ORDER_EXPIRED); + } + } + + OrderAmountVO orderAmountVO = orderService.getOrdersActualAmount(orderIds); + return ServerResponseEntity.success(orderAmountVO); + } + + @Override + public ServerResponseEntity> getOrdersStatus(List orderIds) { + List orderStatusList = orderService.getOrdersStatus(orderIds); + return ServerResponseEntity.success(orderStatusList); + } + + @Override + public ServerResponseEntity> getOrdersSimpleAmountInfo(List orderIds) { + return ServerResponseEntity.success(orderService.getOrdersSimpleAmountInfo(orderIds)); + } + + @Override + public ServerResponseEntity getEsOrder(Long orderId) { + EsOrderBO esOrderBO = orderService.getEsOrder(orderId); + return ServerResponseEntity.success(esOrderBO); + } + + @Override + public ServerResponseEntity updateOrderState(List orderIds) { + return null; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderCancelConsumer.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderCancelConsumer.java new file mode 100644 index 00000000..9557ca1e --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderCancelConsumer.java @@ -0,0 +1,30 @@ +package com.mall4j.cloud.order.listener; + +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.order.service.OrderService; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author FrozenWatermelon + */ +@Component +@RocketMQMessageListener(topic = RocketMqConstant.ORDER_CANCEL_TOPIC,consumerGroup = RocketMqConstant.ORDER_CANCEL_TOPIC) +public class OrderCancelConsumer implements RocketMQListener> { + + @Autowired + private OrderService orderService; + + /** + * 订单取消状态修改后再进行其他服务 + */ + @Override + public void onMessage(List orderIds) { + // 如果订单未支付的话,将订单设为取消状态 + orderService.cancelOrderAndGetCancelOrderIds(orderIds); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderNotifyConsumer.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderNotifyConsumer.java new file mode 100644 index 00000000..46df7532 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/listener/OrderNotifyConsumer.java @@ -0,0 +1,46 @@ +package com.mall4j.cloud.order.listener; + +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.order.bo.PayNotifyBO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.order.service.OrderService; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2021/1/7 + */ +@Component +@RocketMQMessageListener(topic = RocketMqConstant.ORDER_NOTIFY_TOPIC,consumerGroup = RocketMqConstant.ORDER_NOTIFY_TOPIC) +public class OrderNotifyConsumer implements RocketMQListener { + + @Autowired + private OrderService orderService; + @Autowired + private RocketMQTemplate orderNotifyStockTemplate; + + private static final Logger LOG = LoggerFactory.getLogger(OrderNotifyConsumer.class); + + @Override + public void onMessage(PayNotifyBO message) { + LOG.info("订单回调开始... message: " + Json.toJsonString(message)); + orderService.updateByToPaySuccess(message.getOrderIds()); + // 发送消息,订单支付成功 通知库存扣减 + SendStatus sendStockStatus = orderNotifyStockTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC, new GenericMessage<>(message)).getSendStatus(); + if (!Objects.equals(sendStockStatus,SendStatus.SEND_OK)) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderAddrMapper.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderAddrMapper.java new file mode 100644 index 00000000..df4ba5c5 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderAddrMapper.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.order.mapper; + +import com.mall4j.cloud.order.model.OrderAddr; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户订单配送地址 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public interface OrderAddrMapper { + + /** + * 获取用户订单配送地址列表 + * @return 用户订单配送地址列表 + */ + List list(); + + /** + * 根据用户订单配送地址id获取用户订单配送地址 + * + * @param orderAddrId 用户订单配送地址id + * @return 用户订单配送地址 + */ + OrderAddr getByOrderAddrId(@Param("orderAddrId") Long orderAddrId); + + /** + * 保存用户订单配送地址 + * @param orderAddr 用户订单配送地址 + */ + void save(@Param("orderAddr") OrderAddr orderAddr); + + /** + * 更新用户订单配送地址 + * @param orderAddr 用户订单配送地址 + */ + void update(@Param("orderAddr") OrderAddr orderAddr); + + /** + * 根据用户订单配送地址id删除用户订单配送地址 + * @param orderAddrId + */ + void deleteById(@Param("orderAddrId") Long orderAddrId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderItemMapper.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderItemMapper.java new file mode 100644 index 00000000..e549f717 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderItemMapper.java @@ -0,0 +1,67 @@ +package com.mall4j.cloud.order.mapper; + +import com.mall4j.cloud.order.model.OrderItem; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 订单项 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public interface OrderItemMapper { + + /** + * 保存订单项 + * + * @param orderItem 订单项 + */ + void save(@Param("orderItem") OrderItem orderItem); + + /** + * 更新订单项 + * + * @param orderItem 订单项 + */ + void update(@Param("orderItem") OrderItem orderItem); + + /** + * 根据订单项id删除订单项 + * + * @param orderItemId + */ + void deleteById(@Param("orderItemId") Long orderItemId); + + /** + * 批量保存 + * + * @param orderItems 订单项列表 + */ + void saveBatch(@Param("orderItems") List orderItems); + + /** + * 根据订单号获取订单项 + * + * @param orderId 订单id + * @return 订单项 + */ + List listOrderItemsByOrderId(@Param("orderId") Long orderId); + + /** + * 根据订单id获取商品名称 + * + * @param orderIdList 订单id + * @return 商品名称列表 + */ + List getSpuNameListByOrderIds(@Param("orderIdList") long[] orderIdList); + + /** + * 根据订单id获取订单项数量 + * @param orderId + * @return + */ + Integer countByOrderId(@Param("orderId") Long orderId); + +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderMapper.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderMapper.java new file mode 100644 index 00000000..00c25e67 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderMapper.java @@ -0,0 +1,146 @@ +package com.mall4j.cloud.order.mapper; + +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.bo.OrderSimpleAmountInfoBO; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.order.bo.SubmitOrderPayAmountInfoBO; +import com.mall4j.cloud.order.model.Order; +import com.mall4j.cloud.order.vo.OrderCountVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public interface OrderMapper { + + /** + * 更新订单信息 + * + * @param order 订单信息 + */ + void update(@Param("order") Order order); + + /** + * 根据订单信息id删除订单信息 + * + * @param orderId + */ + void deleteById(@Param("orderId") Long orderId); + + /** + * 批量保存订单数据 + * + * @param orders + */ + void saveBatch(@Param("orders") List orders); + + /** + * 查询订单状态 + * + * @param orderIds 多个订单的订单id + * @return 订单状态列表 + */ + List getOrdersStatus(@Param("orderIds") List orderIds); + + /** + * 计算订单实际金额 + * + * @param orderIds 多个订单的订单id + * @return 订单实际金额总和 + */ + OrderAmountVO getOrdersActualAmount(@Param("orderIds") List orderIds); + + /** + * 将订单改为已支付状态 + * + * @param orderIds 订单ids + */ + void updateByToPaySuccess(@Param("orderIds") List orderIds); + + /** + * 获取订单中的金额信息 + * + * @param orderIds 多个订单的订单id + * @return 获取订单中的金额信息 + */ + List getOrdersSimpleAmountInfo(@Param("orderIds") List orderIds); + + /** + * 取消订单 + * + * @param orderIds 订单ids + */ + void cancelOrders(@Param("orderIds") List orderIds); + + /** + * 根据订单号和用户id获取订单 + * + * @param orderId orderId + * @param userId userId + * @return Order + */ + Order getOrderByOrderIdAndUserId(@Param("orderId") Long orderId, @Param("userId") Long userId); + + /** + * 确认收货 + * @param orderId + * @return + */ + int receiptOrder(@Param("orderId") Long orderId); + + + /** + * 根据订单号删除订单 + * + * @param orderId 订单号 + */ + void deleteOrder(@Param("orderId") Long orderId); + + /** + * 根据订单号和店铺id获取订单 + * + * @param orderId orderId + * @param shopId shopId + * @return Order + */ + Order getOrderByOrderIdAndShopId(@Param("orderId") Long orderId, @Param("shopId") Long shopId); + + /** + * 获取订单和订单项的数据 + * + * @param orderId + * @param shopId + * @return + */ + Order getOrderAndOrderItemData(@Param("orderId") Long orderId, @Param("shopId") Long shopId); + + /** + * 根据订单id列表获取订单金额信息 + * + * @param orderIdList 订单id列表 + * @return + */ + SubmitOrderPayAmountInfoBO getSubmitOrderPayAmountInfo(@Param("orderIdList") long[] orderIdList); + + /** + * 获取订单需要保存到es中的数据 + * + * @param orderId 订单id + * @return + */ + EsOrderBO getEsOrder(@Param("orderId") Long orderId); + + /** + * 计算每个订单状态的状态数量 + * + * @param userId 用户id + * @return + */ + OrderCountVO countNumberOfStatus(@Param("userId") Long userId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderPayInfoMapper.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderPayInfoMapper.java new file mode 100644 index 00000000..70e43af6 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/mapper/OrderPayInfoMapper.java @@ -0,0 +1,30 @@ +package com.mall4j.cloud.order.mapper; + +import com.mall4j.cloud.order.model.OrderPayInfo; +import org.apache.ibatis.annotations.Param; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public interface OrderPayInfoMapper { + /** + * 保存订单支付记录 + * @param orderPayInfo 订单支付记录 + */ + void save(@Param("orderPayInfo") OrderPayInfo orderPayInfo); + + /** + * 更新订单支付记录 + * @param orderPayInfo 订单支付记录 + */ + void update(@Param("orderPayInfo") OrderPayInfo orderPayInfo); + + /** + * 根据订单支付记录id删除订单支付记录 + * @param payId + */ + void deleteById(@Param("payId") Long payId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/Order.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/Order.java new file mode 100644 index 00000000..d1e3407f --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/Order.java @@ -0,0 +1,276 @@ +package com.mall4j.cloud.order.model; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public class Order extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + public static final String DISTRIBUTED_ID_KEY = "mall4cloud-order"; + + /** + * 订单ID + */ + private Long orderId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 店铺名称 + */ + private String shopName; + + /** + * 总值 + */ + private Long total; + + /** + * 订单状态 1:待付款 2:待发货 3:待收货(已发货) 5:成功 6:失败 + */ + private Integer status; + + /** + * 配送类型 3:无需快递 + */ + private Integer deliveryType; + + /** + * 用户订单地址Id + */ + private Long orderAddrId; + + /** + * 订单地址 + */ + private OrderAddr orderAddr; + + /** + * 订单关闭原因 1-超时未支付 4-买家取消 15-已通过货到付款交易 + */ + private Integer closeType; + + /** + * 订单商品总数 + */ + private Integer allCount; + + /** + * 付款时间 + */ + private Date payTime; + + /** + * 发货时间 + */ + private Date deliveryTime; + + /** + * 完成时间 + */ + private Date finallyTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 是否已支付,1.已支付0.未支付 + */ + private Integer isPayed; + + /** + * 用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除 + */ + private Integer deleteStatus; + + /** + * 订单项 + */ + private List orderItems; + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public OrderAddr getOrderAddr() { + return orderAddr; + } + + public void setOrderAddr(OrderAddr orderAddr) { + this.orderAddr = orderAddr; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Integer getDeleteStatus() { + return deleteStatus; + } + + public void setDeleteStatus(Integer deleteStatus) { + this.deleteStatus = deleteStatus; + } + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + @Override + public String toString() { + return "Order{" + + "orderId=" + orderId + + ", shopId=" + shopId + + ", userId=" + userId + + ", shopName='" + shopName + '\'' + + ", total=" + total + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", orderAddrId=" + orderAddrId + + ", orderAddr=" + orderAddr + + ", closeType=" + closeType + + ", allCount=" + allCount + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", isPayed=" + isPayed + + ", deleteStatus=" + deleteStatus + + ", orderItems=" + orderItems + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderAddr.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderAddr.java new file mode 100644 index 00000000..fda0f1c6 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderAddr.java @@ -0,0 +1,191 @@ +package com.mall4j.cloud.order.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 用户订单配送地址 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public class OrderAddr extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long orderAddrId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 收货人 + */ + private String consignee; + + /** + * 省ID + */ + private Long provinceId; + + /** + * 省 + */ + private String province; + + /** + * 城市ID + */ + private Long cityId; + + /** + * 城市 + */ + private String city; + + /** + * 区域ID + */ + private Long areaId; + + /** + * 区 + */ + private String area; + + /** + * 地址 + */ + private String addr; + + /** + * 邮编 + */ + private String postCode; + + /** + * 手机 + */ + private String mobile; + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public Long getProvinceId() { + return provinceId; + } + + public void setProvinceId(Long provinceId) { + this.provinceId = provinceId; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public Long getCityId() { + return cityId; + } + + public void setCityId(Long cityId) { + this.cityId = cityId; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public String getPostCode() { + return postCode; + } + + public void setPostCode(String postCode) { + this.postCode = postCode; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + @Override + public String toString() { + return "OrderAddr{" + + "orderAddrId=" + orderAddrId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",userId=" + userId + + ",consignee=" + consignee + + ",provinceId=" + provinceId + + ",province=" + province + + ",cityId=" + cityId + + ",city=" + city + + ",areaId=" + areaId + + ",area=" + area + + ",addr=" + addr + + ",postCode=" + postCode + + ",mobile=" + mobile + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderItem.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderItem.java new file mode 100644 index 00000000..9e814b23 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderItem.java @@ -0,0 +1,216 @@ +package com.mall4j.cloud.order.model; + +import java.io.Serializable; +import java.util.Date; +import com.mall4j.cloud.common.model.BaseModel; +/** + * 订单项 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderItem extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 订单项ID + */ + private Long orderItemId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 订单id + */ + private Long orderId; + + /** + * 产品ID + */ + private Long spuId; + + /** + * 产品SkuID + */ + private Long skuId; + + /** + * 用户Id + */ + private Long userId; + + /** + * 购物车产品个数 + */ + private Integer count; + + /** + * 产品名称 + */ + private String spuName; + + /** + * sku名称 + */ + private String skuName; + + /** + * 产品主图片路径 + */ + private String pic; + + /** + * 单个orderItem的配送类型 3:无需快递 + */ + private Integer deliveryType; + + /** + * 加入购物车时间 + */ + private Date shopCartTime; + + /** + * 产品价格 + */ + private Long price; + + /** + * 商品总金额 + */ + private Long spuTotalAmount; + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Date getShopCartTime() { + return shopCartTime; + } + + public void setShopCartTime(Date shopCartTime) { + this.shopCartTime = shopCartTime; + } + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } + + public Long getSpuTotalAmount() { + return spuTotalAmount; + } + + public void setSpuTotalAmount(Long spuTotalAmount) { + this.spuTotalAmount = spuTotalAmount; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + @Override + public String toString() { + return "OrderItem{" + + "orderItemId=" + orderItemId + + ", shopId=" + shopId + + ", orderId=" + orderId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", userId=" + userId + + ", count=" + count + + ", spuName='" + spuName + '\'' + + ", skuName='" + skuName + '\'' + + ", pic='" + pic + '\'' + + ", deliveryType=" + deliveryType + + ", shopCartTime=" + shopCartTime + + ", price=" + price + + ", spuTotalAmount=" + spuTotalAmount + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderPayInfo.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderPayInfo.java new file mode 100644 index 00000000..feae08b6 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/model/OrderPayInfo.java @@ -0,0 +1,162 @@ +package com.mall4j.cloud.order.model; + +import java.io.Serializable; +import java.util.Date; +import com.mall4j.cloud.common.model.BaseModel; +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderPayInfo extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 支付单号 + */ + private Long payId; + + /** + * 用户id + */ + private Long userId; + + /** + * 外部订单流水号 + */ + private String bizPayNo; + + /** + * 系统类型 见SysTypeEnum + */ + private Integer sysType; + + /** + * 支付状态 + */ + private Integer payStatus; + + /** + * 支付金额 + */ + private Long payAmount; + + /** + * 版本号 + */ + private Integer version; + + /** + * 回调内容 + */ + private String callbackContent; + + /** + * 回调时间 + */ + private Date callbackTime; + + /** + * 确认时间 + */ + private Date confirmTime; + + public Long getPayId() { + return payId; + } + + public void setPayId(Long payId) { + this.payId = payId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getBizPayNo() { + return bizPayNo; + } + + public void setBizPayNo(String bizPayNo) { + this.bizPayNo = bizPayNo; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Integer getPayStatus() { + return payStatus; + } + + public void setPayStatus(Integer payStatus) { + this.payStatus = payStatus; + } + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public String getCallbackContent() { + return callbackContent; + } + + public void setCallbackContent(String callbackContent) { + this.callbackContent = callbackContent; + } + + public Date getCallbackTime() { + return callbackTime; + } + + public void setCallbackTime(Date callbackTime) { + this.callbackTime = callbackTime; + } + + public Date getConfirmTime() { + return confirmTime; + } + + public void setConfirmTime(Date confirmTime) { + this.confirmTime = confirmTime; + } + + @Override + public String toString() { + return "OrderPayInfo{" + + "payId=" + payId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",userId=" + userId + + ",bizPayNo=" + bizPayNo + + ",sysType=" + sysType + + ",payStatus=" + payStatus + + ",payAmount=" + payAmount + + ",version=" + version + + ",callbackContent=" + callbackContent + + ",callbackTime=" + callbackTime + + ",confirmTime=" + confirmTime + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderAddrService.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderAddrService.java new file mode 100644 index 00000000..272380fd --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderAddrService.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.order.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.order.model.OrderAddr; + +/** + * 用户订单配送地址 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public interface OrderAddrService { + + /** + * 分页获取用户订单配送地址列表 + * @param pageDTO 分页参数 + * @return 用户订单配送地址列表分页数据 + */ + PageVO page(PageDTO pageDTO); + + /** + * 根据用户订单配送地址id获取用户订单配送地址 + * + * @param orderAddrId 用户订单配送地址id + * @return 用户订单配送地址 + */ + OrderAddr getByOrderAddrId(Long orderAddrId); + + /** + * 保存用户订单配送地址 + * @param orderAddr 用户订单配送地址 + */ + void save(OrderAddr orderAddr); + + /** + * 更新用户订单配送地址 + * @param orderAddr 用户订单配送地址 + */ + void update(OrderAddr orderAddr); + + /** + * 根据用户订单配送地址id删除用户订单配送地址 + * @param orderAddrId + */ + void deleteById(Long orderAddrId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderItemService.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderItemService.java new file mode 100644 index 00000000..53553aeb --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderItemService.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.order.service; + +import com.mall4j.cloud.order.model.OrderItem; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 订单项 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public interface OrderItemService { + + /** + * 保存订单项 + * + * @param orderItem 订单项 + */ + void save(OrderItem orderItem); + + /** + * 更新订单项 + * + * @param orderItem 订单项 + */ + void update(OrderItem orderItem); + + /** + * 根据订单项id删除订单项 + * + * @param orderItemId + */ + void deleteById(Long orderItemId); + + /** + * 根据订单号获取订单项 + * + * @param orderId 订单id + * @return 订单项 + */ + List listOrderItemsByOrderId(Long orderId); + + /** + * 批量保存订单项信息 + * + * @param orderItems + */ + void saveBatch(List orderItems); + + /** + * 根据订单id获取商品名称 + * + * @param orderIdList 订单id + * @return 商品名称列表 + */ + List getSpuNameListByOrderIds(long[] orderIdList); + + /** + * 根据订单id获取订单项数量 + * @param orderId + * @return + */ + Integer countByOrderId(@Param("orderId") Long orderId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderPayInfoService.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderPayInfoService.java new file mode 100644 index 00000000..2c8cf4f0 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderPayInfoService.java @@ -0,0 +1,30 @@ +package com.mall4j.cloud.order.service; + +import com.mall4j.cloud.order.model.OrderPayInfo; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public interface OrderPayInfoService { + + /** + * 保存订单支付记录 + * @param orderPayInfo 订单支付记录 + */ + void save(OrderPayInfo orderPayInfo); + + /** + * 更新订单支付记录 + * @param orderPayInfo 订单支付记录 + */ + void update(OrderPayInfo orderPayInfo); + + /** + * 根据订单支付记录id删除订单支付记录 + * @param payId + */ + void deleteById(Long payId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderService.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderService.java new file mode 100644 index 00000000..2c766ce3 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/OrderService.java @@ -0,0 +1,138 @@ +package com.mall4j.cloud.order.service; + +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.bo.OrderSimpleAmountInfoBO; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.dto.DeliveryOrderDTO; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.common.order.vo.ShopCartOrderMergerVO; +import com.mall4j.cloud.order.bo.SubmitOrderPayAmountInfoBO; +import com.mall4j.cloud.order.model.Order; +import com.mall4j.cloud.order.vo.OrderCountVO; +import com.mall4j.cloud.order.vo.OrderVO; + +import java.util.List; + +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public interface OrderService { + + /** + * 更新订单信息 + * @param order 订单信息 + */ + void update(Order order); + + /** + * 根据订单信息id删除订单信息 + * @param orderId + */ + void deleteById(Long orderId); + + /** + * 提交订单 + * @param userId + * @param mergerOrder + * @return + */ + List submit(Long userId, ShopCartOrderMergerVO mergerOrder); + + /** + * 查询订单状态 + * @param orderIds 多个订单的订单id + * @return 订单状态列表 + */ + List getOrdersStatus(List orderIds); + + /** + * 计算订单实际金额 + * @param orderIds 多个订单的订单id + * @return 订单实际金额总和 + */ + OrderAmountVO getOrdersActualAmount(List orderIds); + + /** + * 将订单改为已支付状态 + * @param orderIds 订单ids + */ + void updateByToPaySuccess(List orderIds); + + /** + * 获取订单中的金额信息,不包含退款 + * @param orderIds 多个订单的订单id + * @return 订单商家id列表 + */ + List getOrdersSimpleAmountInfo(List orderIds); + + /** + * 取消订单 + * @param orderId 订单id + */ + void cancelOrderAndGetCancelOrderIds(List orderId); + + /** + * 根据订单号和用户id获取订单 + * @param orderId orderId + * @param userId userId + * @return Order + */ + Order getOrderByOrderIdAndUserId(Long orderId, Long userId); + + /** + * 根据订单号和店铺id获取订单 + * @param orderId orderId + * @return Order + */ + OrderVO getOrderByOrderId(Long orderId); + /** + * 更新订单 + * @param orderId 订单号 + * @return + */ + int receiptOrder(Long orderId); + + /** + * 根据订单号删除订单 + * @param orderId 订单号 + */ + void deleteOrder(Long orderId); + + /** + * 订单物流发货 + * @param deliveryOrderParam 发货参数 + */ + void delivery(DeliveryOrderDTO deliveryOrderParam); + + /** + * 根据订单id列表获取订单金额信息 + * @param orderIdList 订单id列表 + * @return + */ + SubmitOrderPayAmountInfoBO getSubmitOrderPayAmountInfo(long[] orderIdList); + + /** + * 获取订单需要保存到es中的数据 + * @param orderId 订单id + * @return + */ + EsOrderBO getEsOrder(Long orderId); + + /** + * 获取订单和订单项信息 + * @param orderId 订单id + * @param shopId 店铺id + * @return + */ + Order getOrderAndOrderItemData(Long orderId,Long shopId); + + /** + * 计算每个订单状态的状态数量 + * @param userId 用户id + * @return + */ + OrderCountVO countNumberOfStatus(Long userId); +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderAddrServiceImpl.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderAddrServiceImpl.java new file mode 100644 index 00000000..88846452 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderAddrServiceImpl.java @@ -0,0 +1,48 @@ +package com.mall4j.cloud.order.service.impl; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.order.mapper.OrderAddrMapper; +import com.mall4j.cloud.order.model.OrderAddr; +import com.mall4j.cloud.order.service.OrderAddrService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 用户订单配送地址 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +@Service +public class OrderAddrServiceImpl implements OrderAddrService { + + @Autowired + private OrderAddrMapper orderAddrMapper; + + @Override + public PageVO page(PageDTO pageDTO) { + return PageUtil.doPage(pageDTO, () -> orderAddrMapper.list()); + } + + @Override + public OrderAddr getByOrderAddrId(Long orderAddrId) { + return orderAddrMapper.getByOrderAddrId(orderAddrId); + } + + @Override + public void save(OrderAddr orderAddr) { + orderAddrMapper.save(orderAddr); + } + + @Override + public void update(OrderAddr orderAddr) { + orderAddrMapper.update(orderAddr); + } + + @Override + public void deleteById(Long orderAddrId) { + orderAddrMapper.deleteById(orderAddrId); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderItemServiceImpl.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderItemServiceImpl.java new file mode 100644 index 00000000..8bb0879f --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderItemServiceImpl.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.order.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.order.model.OrderItem; +import com.mall4j.cloud.order.mapper.OrderItemMapper; +import com.mall4j.cloud.order.service.OrderItemService; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * 订单项 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +@Service +public class OrderItemServiceImpl implements OrderItemService { + + @Autowired + private OrderItemMapper orderItemMapper; + + @Override + public void save(OrderItem orderItem) { + orderItemMapper.save(orderItem); + } + + @Override + public void update(OrderItem orderItem) { + orderItemMapper.update(orderItem); + } + + @Override + public void deleteById(Long orderItemId) { + orderItemMapper.deleteById(orderItemId); + } + + @Override + public List listOrderItemsByOrderId(Long orderId) { + return orderItemMapper.listOrderItemsByOrderId(orderId); + } + + @Override + public void saveBatch(List orderItems) { + if (CollUtil.isEmpty(orderItems)) { + return; + } + orderItemMapper.saveBatch(orderItems); + } + + @Override + public List getSpuNameListByOrderIds(long[] orderIdList) { + return orderItemMapper.getSpuNameListByOrderIds(orderIdList); + } + + @Override + public Integer countByOrderId(Long orderId) { + return orderItemMapper.countByOrderId(orderId); + } + + +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderPayInfoServiceImpl.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderPayInfoServiceImpl.java new file mode 100644 index 00000000..a79923c0 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderPayInfoServiceImpl.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.order.service.impl; + +import com.mall4j.cloud.order.model.OrderPayInfo; +import com.mall4j.cloud.order.mapper.OrderPayInfoMapper; +import com.mall4j.cloud.order.service.OrderPayInfoService; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +@Service +public class OrderPayInfoServiceImpl implements OrderPayInfoService { + + @Autowired + private OrderPayInfoMapper orderPayInfoMapper; + + @Override + public void save(OrderPayInfo orderPayInfo) { + orderPayInfoMapper.save(orderPayInfo); + } + + @Override + public void update(OrderPayInfo orderPayInfo) { + orderPayInfoMapper.update(orderPayInfo); + } + + @Override + public void deleteById(Long payId) { + orderPayInfoMapper.deleteById(payId); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderServiceImpl.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderServiceImpl.java new file mode 100644 index 00000000..9a817c1d --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/service/impl/OrderServiceImpl.java @@ -0,0 +1,326 @@ +package com.mall4j.cloud.order.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.bo.OrderSimpleAmountInfoBO; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.constant.DeliveryType; +import com.mall4j.cloud.api.order.constant.OrderStatus; +import com.mall4j.cloud.api.order.dto.DeliveryOrderDTO; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.api.product.dto.SkuStockLockDTO; +import com.mall4j.cloud.api.product.feign.ShopCartFeignClient; +import com.mall4j.cloud.api.product.feign.SkuStockLockFeignClient; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.common.order.vo.ShopCartOrderMergerVO; +import com.mall4j.cloud.common.order.vo.ShopCartOrderVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.order.bo.SubmitOrderPayAmountInfoBO; +import com.mall4j.cloud.order.mapper.OrderMapper; +import com.mall4j.cloud.order.model.Order; +import com.mall4j.cloud.order.model.OrderAddr; +import com.mall4j.cloud.order.model.OrderItem; +import com.mall4j.cloud.order.service.OrderAddrService; +import com.mall4j.cloud.order.service.OrderItemService; +import com.mall4j.cloud.order.service.OrderService; +import com.mall4j.cloud.order.vo.OrderCountVO; +import com.mall4j.cloud.order.vo.OrderVO; +import io.seata.spring.annotation.GlobalTransactional; +import ma.glasnost.orika.MapperFacade; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +@Service +public class OrderServiceImpl implements OrderService { + private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class); + + + @Autowired + private OrderMapper orderMapper; + @Autowired + private OrderItemService orderItemService; + @Autowired + private SegmentFeignClient segmentFeignClient; + @Autowired + private SkuStockLockFeignClient skuStockLockFeignClient; + @Autowired + private MapperFacade mapperFacade; + @Autowired + private ShopCartFeignClient shopCartFeignClient; + @Autowired + private RocketMQTemplate stockMqTemplate; + @Autowired + private RocketMQTemplate orderCancelTemplate; + @Autowired + private OrderAddrService orderAddrService; + + @Override + public void update(Order order) { + orderMapper.update(order); + } + + @Override + public void deleteById(Long orderId) { + orderMapper.deleteById(orderId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public List submit(Long userId, ShopCartOrderMergerVO mergerOrder) { + List orders = saveOrder(userId, mergerOrder); + List orderIds = new ArrayList<>(); + List skuStockLocks = new ArrayList<>(); + for (Order order : orders) { + orderIds.add(order.getOrderId()); + List orderItems = order.getOrderItems(); + for (OrderItem orderItem : orderItems) { + skuStockLocks.add(new SkuStockLockDTO(orderItem.getSpuId(), orderItem.getSkuId(), orderItem.getOrderId(), orderItem.getCount())); + } + } + // 锁定库存 + ServerResponseEntity lockStockResponse = skuStockLockFeignClient.lock(skuStockLocks); + // 锁定不成,抛异常,让回滚订单 + if (!lockStockResponse.isSuccess()) { + throw new mall4cloudException(lockStockResponse.getMsg()); + } + // 发送消息,如果三十分钟后没有支付,则取消订单 + SendStatus sendStatus = orderCancelTemplate.syncSend(RocketMqConstant.ORDER_CANCEL_TOPIC, new GenericMessage<>(orderIds), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL).getSendStatus(); + if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + return orderIds; + } + + + @Override + public List getOrdersStatus(List orderIds) { + List orderStatusList = orderMapper.getOrdersStatus(orderIds); + for (Long orderId : orderIds) { + boolean hasOrderId = false; + for (OrderStatusBO orderStatusBO : orderStatusList) { + if (Objects.equals(orderStatusBO.getOrderId(), orderId)) { + hasOrderId = true; + } + } + if (!hasOrderId) { + OrderStatusBO orderStatusBO = new OrderStatusBO(); + orderStatusBO.setOrderId(orderId); + orderStatusList.add(orderStatusBO); + } + } + return orderStatusList; + } + + @Override + public OrderAmountVO getOrdersActualAmount(List orderIds) { + return orderMapper.getOrdersActualAmount(orderIds); + } + + @Override + public void updateByToPaySuccess(List orderIds) { + orderMapper.updateByToPaySuccess(orderIds); + } + + @Override + public List getOrdersSimpleAmountInfo(List orderIds) { + return orderMapper.getOrdersSimpleAmountInfo(orderIds); + } + + /** + * 取消订单和mq日志要同时落地,所以用分布式事务 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelOrderAndGetCancelOrderIds(List orderIds) { + List ordersStatus = orderMapper.getOrdersStatus(orderIds); + List cancelOrderIds = new ArrayList<>(); + for (OrderStatusBO orderStatusBO : ordersStatus) { + if (orderStatusBO.getStatus() != null || !Objects.equals(orderStatusBO.getStatus(), OrderStatus.CLOSE.value())) { + cancelOrderIds.add(orderStatusBO.getOrderId()); + } + } + if (CollectionUtil.isEmpty(cancelOrderIds)) { + return; + } + orderMapper.cancelOrders(cancelOrderIds); + // 解锁库存状态 + SendStatus stockSendStatus = stockMqTemplate.syncSend(RocketMqConstant.STOCK_UNLOCK_TOPIC, new GenericMessage<>(orderIds)).getSendStatus(); + if (!Objects.equals(stockSendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + + @Override + public Order getOrderByOrderIdAndUserId(Long orderId, Long userId) { + Order order = orderMapper.getOrderByOrderIdAndUserId(orderId, userId); + if (order == null) { + // 订单不存在 + throw new mall4cloudException(ResponseEnum.ORDER_NOT_EXIST); + } + return order; + } + + @Override + public OrderVO getOrderByOrderId(Long orderId) { + Order order = orderMapper.getOrderByOrderIdAndShopId(orderId, AuthUserContext.get().getTenantId()); + if (order == null) { + // 订单不存在 + throw new mall4cloudException(ResponseEnum.ORDER_NOT_EXIST); + } + return mapperFacade.map(order, OrderVO.class); + } + + /** + * 确认收货订单 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int receiptOrder(Long orderId) { + return orderMapper.receiptOrder(orderId); + } + + + @Override + public void deleteOrder(Long orderId) { + orderMapper.deleteOrder(orderId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @GlobalTransactional(rollbackFor = Exception.class) + public void delivery(DeliveryOrderDTO deliveryOrderParam) { + //修改为发货状态 + Date date = new Date(); + Order order = new Order(); + order.setOrderId(deliveryOrderParam.getOrderId()); + order.setStatus(OrderStatus.CONSIGNMENT.value()); + order.setUpdateTime(date); + order.setDeliveryTime(date); + //无需物流 + order.setDeliveryType(DeliveryType.NOT_DELIVERY.value()); + orderMapper.update(order); + + } + + @Override + public SubmitOrderPayAmountInfoBO getSubmitOrderPayAmountInfo(long[] orderIdList) { + return orderMapper.getSubmitOrderPayAmountInfo(orderIdList); + } + + @Override + public EsOrderBO getEsOrder(Long orderId) { + return orderMapper.getEsOrder(orderId); + } + + + public List saveOrder(Long userId, ShopCartOrderMergerVO mergerOrder) { + OrderAddr orderAddr = mapperFacade.map(mergerOrder.getUserAddr(), OrderAddr.class); + // 地址信息 + if (Objects.isNull(orderAddr)) { + // 请填写收货地址 + throw new mall4cloudException("请填写收货地址"); + } + // 保存收货地址 + orderAddrService.save(orderAddr); + // 订单商品参数 + List shopCartOrders = mergerOrder.getShopCartOrders(); + List orders = new ArrayList<>(); + List orderItems = new ArrayList<>(); + List shopCartItemIds = new ArrayList<>(); + if(CollectionUtil.isNotEmpty(shopCartOrders)) { + // 每个店铺生成一个订单 + for (ShopCartOrderVO shopCartOrderDto : shopCartOrders) { + Order order = getOrder(userId, mergerOrder.getDvyType(), shopCartOrderDto); + for (ShopCartItemVO shopCartItemVO : shopCartOrderDto.getShopCartItemVO()) { + OrderItem orderItem = getOrderItem(order, shopCartItemVO); + orderItems.add(orderItem); + shopCartItemIds.add(shopCartItemVO.getCartItemId()); + } + order.setOrderItems(orderItems); + order.setOrderAddrId(orderAddr.getOrderAddrId()); + orders.add(order); + } + } + orderMapper.saveBatch(orders); + orderItemService.saveBatch(orderItems); + // 清空购物车 + shopCartFeignClient.deleteItem(shopCartItemIds); + return orders; + } + + private OrderItem getOrderItem(Order order, ShopCartItemVO shopCartItem) { + OrderItem orderItem = new OrderItem(); + orderItem.setOrderId(order.getOrderId()); + orderItem.setShopId(shopCartItem.getShopId()); + orderItem.setSkuId(shopCartItem.getSkuId()); + orderItem.setSpuId(shopCartItem.getSpuId()); + orderItem.setSkuName(shopCartItem.getSkuName()); + orderItem.setCount(shopCartItem.getCount()); + orderItem.setSpuName(shopCartItem.getSpuName()); + orderItem.setPic(shopCartItem.getImgUrl()); + orderItem.setPrice(shopCartItem.getSkuPriceFee()); + orderItem.setUserId(order.getUserId()); + orderItem.setSpuTotalAmount(shopCartItem.getTotalAmount()); + orderItem.setShopCartTime(shopCartItem.getCreateTime()); + // 订单项支付金额 + orderItem.setSpuTotalAmount(shopCartItem.getTotalAmount()); + return orderItem; + } + + private Order getOrder(Long userId, Integer dvyType, ShopCartOrderVO shopCartOrderDto) { + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(Order.DISTRIBUTED_ID_KEY); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException("获取订单id失败"); + } + // 订单信息 + Order order = new Order(); + order.setOrderId(segmentIdResponse.getData()); + order.setShopId(shopCartOrderDto.getShopId()); + order.setShopName(shopCartOrderDto.getShopName()); + + // 用户id + order.setUserId(userId); + // 商品总额 + order.setTotal(shopCartOrderDto.getTotal()); + order.setStatus(OrderStatus.UNPAY.value()); + order.setIsPayed(0); + order.setDeleteStatus(0); + order.setAllCount(shopCartOrderDto.getTotalCount()); + order.setDeliveryType(DeliveryType.NOT_DELIVERY.value()); + return order; + } + + @Override + public Order getOrderAndOrderItemData(Long orderId, Long shopId) { + return orderMapper.getOrderAndOrderItemData(orderId, shopId); + } + + @Override + public OrderCountVO countNumberOfStatus(Long userId) { + return orderMapper.countNumberOfStatus(userId); + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderItemVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderItemVO.java new file mode 100644 index 00000000..a9a093bc --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderItemVO.java @@ -0,0 +1,127 @@ +package com.mall4j.cloud.order.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + */ +@ApiModel("我的订单-订单项") +public class MyOrderItemVO { + + @ApiModelProperty(value = "商品图片", required = true) + @JsonSerialize(using = ImgJsonSerializer.class) + private String pic; + + @ApiModelProperty(value = "商品名称", required = true) + private String spuName; + + @ApiModelProperty(value = "订单号",required=true) + private Long orderId; + + @ApiModelProperty(value = "商品数量", required = true) + private Integer count; + + @ApiModelProperty(value = "商品价格", required = true) + private Long price; + + @ApiModelProperty(value = "skuId", required = true) + private Long skuId; + + @ApiModelProperty(value = "skuName", required = true) + private String skuName; + + @ApiModelProperty(value = "订单项id", required = true) + private Long orderItemId; + + @ApiModelProperty(value = "商品id", required = true) + private Long spuId; + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + @Override + public String toString() { + return "MyOrderItemVO{" + + "pic='" + pic + '\'' + + ", spuName='" + spuName + '\'' + + ", orderId=" + orderId + + ", count=" + count + + ", price=" + price + + ", skuId=" + skuId + + ", skuName='" + skuName + '\'' + + ", orderItemId=" + orderItemId + + ", spuId=" + spuId + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderVO.java new file mode 100644 index 00000000..b8bd842e --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/MyOrderVO.java @@ -0,0 +1,130 @@ +package com.mall4j.cloud.order.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * 我的订单 + * + * @author FrozenWatermelon + */ +@ApiModel("我的订单") +public class MyOrderVO { + + @ApiModelProperty(value = "订单项",required=true) + private List orderItems; + + @ApiModelProperty(value = "订单号",required=true) + private Long orderId; + + @ApiModelProperty(value = "总价",required=true) + private Long actualTotal; + + @ApiModelProperty(value = "订单状态",required=true) + private Integer status; + + @ApiModelProperty(value = "配送类型 3:无需快递",required=true) + private Integer deliveryType; + + @ApiModelProperty(value = "店铺名称",required=true) + private String shopName; + + @ApiModelProperty(value = "店铺id",required=true) + private Long shopId; + + @ApiModelProperty(value = "订单创建时间",required=true) + private Date createTime; + + @ApiModelProperty(value = "商品总数",required=true) + private Integer allCount; + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getActualTotal() { + return actualTotal; + } + + public void setActualTotal(Long actualTotal) { + this.actualTotal = actualTotal; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + @Override + public String toString() { + return "MyOrderVO{" + + "orderItems=" + orderItems + + ", orderId=" + orderId + + ", actualTotal=" + actualTotal + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", shopName='" + shopName + '\'' + + ", shopId=" + shopId + + ", createTime=" + createTime + + ", allCount=" + allCount + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderAddrVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderAddrVO.java new file mode 100644 index 00000000..35b1f378 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderAddrVO.java @@ -0,0 +1,166 @@ +package com.mall4j.cloud.order.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 用户订单配送地址VO + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public class OrderAddrVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long orderAddrId; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("收货人") + private String consignee; + + @ApiModelProperty("省ID") + private Long provinceId; + + @ApiModelProperty("省") + private String province; + + @ApiModelProperty("城市ID") + private Long cityId; + + @ApiModelProperty("城市") + private String city; + + @ApiModelProperty("区域ID") + private Long areaId; + + @ApiModelProperty("区") + private String area; + + @ApiModelProperty("地址") + private String addr; + + @ApiModelProperty("邮编") + private String postCode; + + @ApiModelProperty("手机") + private String mobile; + + public Long getOrderAddrId() { + return orderAddrId; + } + + public void setOrderAddrId(Long orderAddrId) { + this.orderAddrId = orderAddrId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public Long getProvinceId() { + return provinceId; + } + + public void setProvinceId(Long provinceId) { + this.provinceId = provinceId; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public Long getCityId() { + return cityId; + } + + public void setCityId(Long cityId) { + this.cityId = cityId; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public String getPostCode() { + return postCode; + } + + public void setPostCode(String postCode) { + this.postCode = postCode; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + @Override + public String toString() { + return "OrderAddrVO{" + + "orderAddrId=" + orderAddrId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",userId=" + userId + + ",consignee=" + consignee + + ",provinceId=" + provinceId + + ",province=" + province + + ",cityId=" + cityId + + ",city=" + city + + ",areaId=" + areaId + + ",area=" + area + + ",addr=" + addr + + ",postCode=" + postCode + + ",mobile=" + mobile + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderCountVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderCountVO.java new file mode 100644 index 00000000..6288f83b --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderCountVO.java @@ -0,0 +1,77 @@ +package com.mall4j.cloud.order.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + */ +@ApiModel("我的订单数量") +public class OrderCountVO { + + @ApiModelProperty(value = "所有订单数量") + private Integer allCount; + + @ApiModelProperty(value = "待付款") + private Integer unPay; + + @ApiModelProperty(value = "待发货") + private Integer payed; + + @ApiModelProperty(value = "待收货") + private Integer consignment; + + @ApiModelProperty(value = "已完成") + private Integer success; + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public Integer getUnPay() { + return unPay; + } + + public void setUnPay(Integer unPay) { + this.unPay = unPay; + } + + public Integer getPayed() { + return payed; + } + + public void setPayed(Integer payed) { + this.payed = payed; + } + + public Integer getConsignment() { + return consignment; + } + + public void setConsignment(Integer consignment) { + this.consignment = consignment; + } + + public Integer getSuccess() { + return success; + } + + public void setSuccess(Integer success) { + this.success = success; + } + + @Override + public String toString() { + return "OrderCountVO{" + + "allCount=" + allCount + + ", unPay=" + unPay + + ", payed=" + payed + + ", consignment=" + consignment + + ", success=" + success + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderItemVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderItemVO.java new file mode 100644 index 00000000..cb693f8a --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderItemVO.java @@ -0,0 +1,201 @@ +package com.mall4j.cloud.order.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; + +/** + * 订单项VO + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderItemVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("订单项ID") + private Long orderItemId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("订单id") + private Long orderId; + + @ApiModelProperty("产品ID") + private Long spuId; + + @ApiModelProperty("产品SkuID") + private Long skuId; + + @ApiModelProperty("用户Id") + private Long userId; + + @ApiModelProperty("购物车产品个数") + private Integer count; + + @ApiModelProperty("产品名称") + private String spuName; + + @ApiModelProperty("sku名称") + private String skuName; + + @ApiModelProperty("产品主图片路径") + private String pic; + + @ApiModelProperty("单个orderItem的配送类型 3:无需快递") + private Integer deliveryType; + + @ApiModelProperty("加入购物车时间") + private Date shopCartTime; + + @ApiModelProperty("产品价格") + private Long price; + + @ApiModelProperty("商品总金额") + private Long spuTotalAmount; + + @ApiModelProperty("发货改变的数量") + private Integer changeNum; + + public Long getOrderItemId() { + return orderItemId; + } + + public void setOrderItemId(Long orderItemId) { + this.orderItemId = orderItemId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Date getShopCartTime() { + return shopCartTime; + } + + public void setShopCartTime(Date shopCartTime) { + this.shopCartTime = shopCartTime; + } + + public Long getPrice() { + return price; + } + + public void setPrice(Long price) { + this.price = price; + } + + public Long getSpuTotalAmount() { + return spuTotalAmount; + } + + public void setSpuTotalAmount(Long spuTotalAmount) { + this.spuTotalAmount = spuTotalAmount; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Integer getChangeNum() { + return changeNum; + } + + public void setChangeNum(Integer changeNum) { + this.changeNum = changeNum; + } + + @Override + public String toString() { + return "OrderItemVO{" + + "orderItemId=" + orderItemId + + ", shopId=" + shopId + + ", orderId=" + orderId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", userId=" + userId + + ", count=" + count + + ", spuName='" + spuName + '\'' + + ", skuName='" + skuName + '\'' + + ", pic='" + pic + '\'' + + ", deliveryType=" + deliveryType + + ", shopCartTime=" + shopCartTime + + ", price=" + price + + ", spuTotalAmount=" + spuTotalAmount + + ", changeNum=" + changeNum + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderPayInfoVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderPayInfoVO.java new file mode 100644 index 00000000..6e008fc1 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderPayInfoVO.java @@ -0,0 +1,143 @@ +package com.mall4j.cloud.order.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; + +/** + * 订单支付记录VO + * + * @author FrozenWatermelon + * @date 2020-12-04 11:27:35 + */ +public class OrderPayInfoVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("支付单号") + private Long payId; + + @ApiModelProperty("用户id") + private Long userId; + + @ApiModelProperty("外部订单流水号") + private String bizPayNo; + + @ApiModelProperty("系统类型 见SysTypeEnum") + private Integer sysType; + + @ApiModelProperty("支付状态") + private Integer payStatus; + + @ApiModelProperty("支付金额") + private Long payAmount; + + @ApiModelProperty("版本号") + private Integer version; + + @ApiModelProperty("回调内容") + private String callbackContent; + + @ApiModelProperty("回调时间") + private Date callbackTime; + + @ApiModelProperty("确认时间") + private Date confirmTime; + + public Long getPayId() { + return payId; + } + + public void setPayId(Long payId) { + this.payId = payId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getBizPayNo() { + return bizPayNo; + } + + public void setBizPayNo(String bizPayNo) { + this.bizPayNo = bizPayNo; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Integer getPayStatus() { + return payStatus; + } + + public void setPayStatus(Integer payStatus) { + this.payStatus = payStatus; + } + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public String getCallbackContent() { + return callbackContent; + } + + public void setCallbackContent(String callbackContent) { + this.callbackContent = callbackContent; + } + + public Date getCallbackTime() { + return callbackTime; + } + + public void setCallbackTime(Date callbackTime) { + this.callbackTime = callbackTime; + } + + public Date getConfirmTime() { + return confirmTime; + } + + public void setConfirmTime(Date confirmTime) { + this.confirmTime = confirmTime; + } + + @Override + public String toString() { + return "OrderPayInfoVO{" + + "payId=" + payId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",userId=" + userId + + ",bizPayNo=" + bizPayNo + + ",sysType=" + sysType + + ",payStatus=" + payStatus + + ",payAmount=" + payAmount + + ",version=" + version + + ",callbackContent=" + callbackContent + + ",callbackTime=" + callbackTime + + ",confirmTime=" + confirmTime + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderShopVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderShopVO.java new file mode 100644 index 00000000..d743e1b3 --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderShopVO.java @@ -0,0 +1,190 @@ +package com.mall4j.cloud.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 订单下的每个店铺 + * + * @author FrozenWatermelon + */ +public class OrderShopVO implements Serializable { + + @ApiModelProperty(value = "店铺id", required = true) + private Long shopId; + + @ApiModelProperty(value = "店铺名称", required = true) + private String shopName; + + @ApiModelProperty(value = "商品总值", required = true) + private Long total; + + @ApiModelProperty(value = "商品总数", required = true) + private Integer totalNum; + + + @ApiModelProperty(value = "地址Dto", required = true) + private OrderAddrVO orderAddr; + + @ApiModelProperty(value = "产品信息", required = true) + private List orderItems; + + @ApiModelProperty(value = "订单创建时间", required = true) + private Date createTime; + + @ApiModelProperty(value = "订单付款时间", required = false) + private Date payTime; + + @ApiModelProperty(value = "订单发货时间", required = false) + private Date deliveryTime; + + @ApiModelProperty(value = "订单完成时间", required = false) + private Date finallyTime; + + @ApiModelProperty(value = "订单取消时间", required = false) + private Date cancelTime; + + @ApiModelProperty(value = "订单更新时间", required = false) + private Date updateTime; + + @ApiModelProperty(value = "配送类型 3:无需快递", required = true) + private Integer deliveryType; + + @ApiModelProperty(value = "订单状态", required = true) + private Integer status; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getTotalNum() { + return totalNum; + } + + public void setTotalNum(Integer totalNum) { + this.totalNum = totalNum; + } + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public OrderAddrVO getOrderAddr() { + return orderAddr; + } + + public void setOrderAddr(OrderAddrVO orderAddr) { + this.orderAddr = orderAddr; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + @Override + public String toString() { + return "OrderShopVO{" + + "shopId=" + shopId + + ", shopName='" + shopName + '\'' + + ", total=" + total + + ", totalNum=" + totalNum + + ", orderAddr=" + orderAddr + + ", orderItems=" + orderItems + + ", createTime=" + createTime + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", updateTime=" + updateTime + + ", deliveryType=" + deliveryType + + ", status=" + status + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderVO.java new file mode 100644 index 00000000..7c56003f --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/OrderVO.java @@ -0,0 +1,226 @@ +package com.mall4j.cloud.order.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * 订单信息 + * + * @author FrozenWatermelon + * @date 2020-12-05 14:13:50 + */ +public class OrderVO extends BaseVO { + + @ApiModelProperty(value = "订单ID") + private Long orderId; + + @ApiModelProperty(value = "店铺id") + private Long shopId; + + @ApiModelProperty(value = "用户ID") + private Long userId; + + @ApiModelProperty(value = "店铺名称") + private String shopName; + + @ApiModelProperty(value = "总值") + private Long total; + + @ApiModelProperty(value = "订单状态 1:待付款 2:待发货 3:待收货(已发货) 5:成功 6:失败") + private Integer status; + + @ApiModelProperty(value = "配送类型 3:无需快递") + private Integer deliveryType; + + @ApiModelProperty(value = "订单关闭原因 1-超时未支付 4-买家取消 15-已通过货到付款交易") + private Integer closeType; + + @ApiModelProperty(value = "订单商品总数") + private Integer allCount; + + @ApiModelProperty(value = "付款时间") + private Date payTime; + + @ApiModelProperty(value = "发货时间") + private Date deliveryTime; + + @ApiModelProperty(value = "完成时间") + private Date finallyTime; + + @ApiModelProperty(value = "取消时间") + private Date cancelTime; + + @ApiModelProperty(value = "是否已支付,1.已支付0.未支付") + private Integer isPayed; + + @ApiModelProperty(value = "用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除") + private Integer deleteStatus; + + @ApiModelProperty(value = "订单项") + private List orderItems; + + @ApiModelProperty(value = "订单地址") + private OrderAddrVO orderAddr; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Integer getDeleteStatus() { + return deleteStatus; + } + + public void setDeleteStatus(Integer deleteStatus) { + this.deleteStatus = deleteStatus; + } + + public List getOrderItems() { + return orderItems; + } + + public void setOrderItems(List orderItems) { + this.orderItems = orderItems; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public OrderAddrVO getOrderAddr() { + return orderAddr; + } + + public void setOrderAddr(OrderAddrVO orderAddr) { + this.orderAddr = orderAddr; + } + + @Override + public String toString() { + return "OrderVO{" + + "orderId=" + orderId + + ", shopId=" + shopId + + ", userId=" + userId + + ", shopName='" + shopName + '\'' + + ", total=" + total + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", closeType=" + closeType + + ", allCount=" + allCount + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", isPayed=" + isPayed + + ", deleteStatus=" + deleteStatus + + ", orderItems=" + orderItems + + ", orderAddr=" + orderAddr + + '}'; + } +} diff --git a/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/SubmitOrderPayInfoVO.java b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/SubmitOrderPayInfoVO.java new file mode 100644 index 00000000..9c1eef8d --- /dev/null +++ b/mall4cloud-order/src/main/java/com/mall4j/cloud/order/vo/SubmitOrderPayInfoVO.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.order.vo; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * 提交订单的支付信息 + * @author FrozenWatermelon + * @date 2021/2/4 + */ +public class SubmitOrderPayInfoVO { + + @ApiModelProperty(value = "商品名称") + private List spuNameList; + + @ApiModelProperty(value = "收货人姓名") + private String consignee; + + @ApiModelProperty(value = "收货地址") + private String userAddr; + + @ApiModelProperty(value = "收货人手机号") + private String mobile; + + @ApiModelProperty(value = "订单过期时间") + private Date endTime; + + @ApiModelProperty(value = "总共需要支付金额") + private Long totalFee; + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Long getTotalFee() { + return totalFee; + } + + public void setTotalFee(Long totalFee) { + this.totalFee = totalFee; + } + + public List getSpuNameList() { + return spuNameList; + } + + public void setSpuNameList(List spuNameList) { + this.spuNameList = spuNameList; + } + + public String getUserAddr() { + return userAddr; + } + + public void setUserAddr(String userAddr) { + this.userAddr = userAddr; + } + + @Override + public String toString() { + return "SubmitOrderPayInfoVO{" + + "spuNameList=" + spuNameList + + ", consignee='" + consignee + '\'' + + ", userAddr='" + userAddr + '\'' + + ", mobile='" + mobile + '\'' + + ", endTime=" + endTime + + ", totalFee=" + totalFee + + '}'; + } +} diff --git a/mall4cloud-order/src/main/resources/bootstrap.yml b/mall4cloud-order/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..c0e937f3 --- /dev/null +++ b/mall4cloud-order/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +server: + port: 9106 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ +mall4cloud: + job: + executor: + # job的端口,比自己的端口小100,就不会冲突咯 + appname: @artifactId@ + port: 9006 + logpath: + logretentiondays: 30 diff --git a/mall4cloud-order/src/main/resources/mapper/OrderAddrMapper.xml b/mall4cloud-order/src/main/resources/mapper/OrderAddrMapper.xml new file mode 100644 index 00000000..b6eb2345 --- /dev/null +++ b/mall4cloud-order/src/main/resources/mapper/OrderAddrMapper.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + `order_addr_id`,`create_time`,`update_time`,`user_id`,`consignee`,`province_id`,`province`,`city_id`,`city`,`area_id`,`area`,`addr`,`post_code`,`mobile` + + + + + insert into order_addr (`user_id`,`consignee`,`province_id`,`province`,`city_id`,`city`,`area_id`,`area`,`addr`,`post_code`,`mobile`) + values (#{orderAddr.userId},#{orderAddr.consignee},#{orderAddr.provinceId},#{orderAddr.province},#{orderAddr.cityId},#{orderAddr.city},#{orderAddr.areaId},#{orderAddr.area},#{orderAddr.addr},#{orderAddr.postCode},#{orderAddr.mobile}); + + + update order_addr + + + `user_id` = #{orderAddr.userId}, + + + `consignee` = #{orderAddr.consignee}, + + + `province_id` = #{orderAddr.provinceId}, + + + `province` = #{orderAddr.province}, + + + `city_id` = #{orderAddr.cityId}, + + + `city` = #{orderAddr.city}, + + + `area_id` = #{orderAddr.areaId}, + + + `area` = #{orderAddr.area}, + + + `addr` = #{orderAddr.addr}, + + + `post_code` = #{orderAddr.postCode}, + + + `mobile` = #{orderAddr.mobile}, + + + where order_addr_id = #{orderAddr.orderAddrId} + + + delete from order_addr where order_addr_id = #{orderAddrId} + + + diff --git a/mall4cloud-order/src/main/resources/mapper/OrderItemMapper.xml b/mall4cloud-order/src/main/resources/mapper/OrderItemMapper.xml new file mode 100644 index 00000000..12d63dac --- /dev/null +++ b/mall4cloud-order/src/main/resources/mapper/OrderItemMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + `order_item_id` + , + `create_time`, + `update_time`, + `shop_id`, + `order_id`, + `spu_id`, + `sku_id`, + `user_id`, + `count`, + `spu_name`, + `sku_name`, + `pic`, + `delivery_type`, + `shop_cart_time`, + `price`, + `spu_total_amount` + + + insert into order_item (`shop_id`, `order_id`, `spu_id`, `sku_id`, `user_id`, `count`, + `spu_name`, `sku_name`, `pic`, `delivery_type`, + `shop_cart_time`, `price`, `spu_total_amount`) + values (#{orderItem.shopId}, #{orderItem.orderId}, #{orderItem.spuId}, #{orderItem.skuId}, #{orderItem.userId}, + #{orderItem.count}, #{orderItem.spuName}, #{orderItem.skuName}, #{orderItem.pic}, + #{orderItem.deliveryType}, #{orderItem.shopCartTime}, #{orderItem.price}, #{orderItem.spuTotalAmount}); + + + update order_item + + + `shop_id` = #{orderItem.shopId}, + + + `order_id` = #{orderItem.orderId}, + + + `spu_id` = #{orderItem.spuId}, + + + `sku_id` = #{orderItem.skuId}, + + + `user_id` = #{orderItem.userId}, + + + `count` = #{orderItem.count}, + + + `spu_name` = #{orderItem.spuName}, + + + `sku_name` = #{orderItem.skuName}, + + + `pic` = #{orderItem.pic}, + + + `delivery_type` = #{orderItem.deliveryType}, + + + `shop_cart_time` = #{orderItem.shopCartTime}, + + + `price` = #{orderItem.price}, + + + `spu_total_amount` = #{orderItem.spuTotalAmount} + + + where order_item_id = #{orderItem.orderItemId} + + + delete + from order_item + where order_item_id = #{orderItemId} + + + insert into order_item (`shop_id`, `order_id`, `spu_id`, `sku_id`, `user_id`, `count`, `spu_name`, + `sku_name`, `pic`,`delivery_type`, `shop_cart_time`, + `price`,`spu_total_amount`) + values + + (#{orderItem.shopId}, #{orderItem.orderId}, #{orderItem.spuId}, #{orderItem.skuId}, #{orderItem.userId}, + #{orderItem.count}, #{orderItem.spuName},#{orderItem.skuName}, #{orderItem.pic}, + #{orderItem.deliveryType}, #{orderItem.shopCartTime}, #{orderItem.price},#{orderItem.spuTotalAmount}) + + + + + + diff --git a/mall4cloud-order/src/main/resources/mapper/OrderMapper.xml b/mall4cloud-order/src/main/resources/mapper/OrderMapper.xml new file mode 100644 index 00000000..4c6d49ea --- /dev/null +++ b/mall4cloud-order/src/main/resources/mapper/OrderMapper.xml @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `order_id`, + `create_time`, + `update_time`, + `shop_id`, + `user_id`, + `order_addr_id`, + `shop_name`, + `total`, + `status`, + `delivery_type`, + `close_type`, + `all_count`, + `pay_time`, + `delivery_time`, + `finally_time`, + `cancel_time`, + `is_payed`, + `delete_status` + + + update `order` + + + `shop_id` = #{order.shopId}, + + + `shop_name` = #{order.shopName}, + + + `user_id` = #{order.userId}, + + + `order_addr_id` = #{order.orderAddrId}, + + + `total` = #{order.total}, + + + `status` = #{order.status}, + + + `delivery_type` = #{order.deliveryType}, + + + `close_type` = #{order.closeType}, + + + `all_count` = #{order.allCount}, + + + `pay_time` = #{order.payTime}, + + + `delivery_time` = #{order.deliveryTime}, + + + `finally_time` = #{order.finallyTime}, + + + `cancel_time` = #{order.cancelTime}, + + + `is_payed` = #{order.isPayed}, + + + `delete_status` = #{order.deleteStatus} + + + where order_id = #{order.orderId} + + + delete from `order` where order_id = #{orderId} + + + insert into `order` (`order_id`, `shop_id`, `shop_name`, `user_id`, `order_addr_id`, `total`, `status`, `delivery_type`,`close_type`, + `all_count`, `pay_time`, `delivery_time`, `finally_time`, `cancel_time`, `is_payed`, `delete_status`) + values + + (#{order.orderId}, #{order.shopId}, #{order.shopName}, #{order.userId}, #{order.orderAddrId}, #{order.total}, #{order.status}, #{order.deliveryType}, + #{order.closeType}, #{order.allCount}, #{order.payTime}, #{order.deliveryTime}, #{order.finallyTime}, + #{order.cancelTime}, #{order.isPayed}, #{order.deleteStatus}) + + + + + + update `order` set `status` = 2,is_payed =1,update_time=NOW(),pay_time=NOW() + where order_id in + + #{orderId} + + + + + update `order` set `status`=6,cancel_time = NOW(),update_time=NOW() where is_payed = 0 and order_id in + + #{orderId} + + + + + update `order` + set `status` = 5, + finally_time = NOW(), + update_time = NOW() + where order_id = #{orderId} and `status` = 3 + + + UPDATE `order` SET `delete_status` = 2 where order_id = #{orderId} + + + + + + + + diff --git a/mall4cloud-order/src/main/resources/mapper/OrderPayInfoMapper.xml b/mall4cloud-order/src/main/resources/mapper/OrderPayInfoMapper.xml new file mode 100644 index 00000000..908e8525 --- /dev/null +++ b/mall4cloud-order/src/main/resources/mapper/OrderPayInfoMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + insert into order_pay_info (`user_id`,`biz_pay_no`,`sys_type`,`pay_type`,`pay_status`,`pay_score`,`pay_amount`,`version`,`callback_content`,`callback_time`,`confirm_time`) + values (#{orderPayInfo.userId},#{orderPayInfo.bizPayNo},#{orderPayInfo.sysType},#{orderPayInfo.payType},#{orderPayInfo.payStatus},#{orderPayInfo.payScore},#{orderPayInfo.payAmount},#{orderPayInfo.version},#{orderPayInfo.callbackContent},#{orderPayInfo.callbackTime},#{orderPayInfo.confirmTime}); + + + update order_pay_info + + + `user_id` = #{orderPayInfo.userId}, + + + `biz_pay_no` = #{orderPayInfo.bizPayNo}, + + + `sys_type` = #{orderPayInfo.sysType}, + + + `pay_status` = #{orderPayInfo.payStatus}, + + + `pay_amount` = #{orderPayInfo.payAmount}, + + + `version` = #{orderPayInfo.version}, + + + `callback_content` = #{orderPayInfo.callbackContent}, + + + `callback_time` = #{orderPayInfo.callbackTime}, + + + `confirm_time` = #{orderPayInfo.confirmTime}, + + + where pay_id = #{orderPayInfo.payId} + + + delete from order_pay_info where pay_id = #{payId} + + diff --git a/mall4cloud-payment/pom.xml b/mall4cloud-payment/pom.xml new file mode 100644 index 00000000..dc737232 --- /dev/null +++ b/mall4cloud-payment/pom.xml @@ -0,0 +1,70 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-payment + mall4cloud 支付服务 + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-rocketmq + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-leaf + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/PaymentApplication.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/PaymentApplication.java new file mode 100644 index 00000000..76a7de66 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/PaymentApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.payment; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/09/22 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class PaymentApplication { + + public static void main(String[] args) { + SpringApplication.run(PaymentApplication.class, args); + } + +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoBO.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoBO.java new file mode 100644 index 00000000..90be5b57 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoBO.java @@ -0,0 +1,98 @@ +package com.mall4j.cloud.payment.bo; + +/** + * @author FrozenWatermelon + * @date 2020/12/25 + */ +public class PayInfoBO { + + /** + * 支付信息,如商品名称 + */ + private String body; + + /** + * 支付单号 + */ + private Long payId; + + /** + * 付款金额 + */ + private Long payAmount; + + /** + * api回调域名 + */ + private String apiNoticeUrl; + + /** + * 支付完成会跳地址 + */ + private String returnUrl; + + /** + * 第三方用户id + */ + private String bizUserId; + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Long getPayId() { + return payId; + } + + public void setPayId(Long payId) { + this.payId = payId; + } + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + public String getApiNoticeUrl() { + return apiNoticeUrl; + } + + public void setApiNoticeUrl(String apiNoticeUrl) { + this.apiNoticeUrl = apiNoticeUrl; + } + + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + this.returnUrl = returnUrl; + } + + public String getBizUserId() { + return bizUserId; + } + + public void setBizUserId(String bizUserId) { + this.bizUserId = bizUserId; + } + + @Override + public String toString() { + return "PayInfoBO{" + + "body='" + body + '\'' + + ", payId='" + payId + '\'' + + ", payAmount=" + payAmount + + ", apiNoticeUrl='" + apiNoticeUrl + '\'' + + ", returnUrl='" + returnUrl + '\'' + + ", bizUserId='" + bizUserId + '\'' + + '}'; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoResultBO.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoResultBO.java new file mode 100644 index 00000000..76bac58f --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/bo/PayInfoResultBO.java @@ -0,0 +1,99 @@ +package com.mall4j.cloud.payment.bo; + +/** + * 支付后返回的一些基础数据 + * @author FrozenWatermelon + * @date 2020/12/25 + */ +public class PayInfoResultBO { + + /** + * 商城支付单号 + */ + private Long payId; + + /** + * 第三方订单流水号 + */ + private String bizPayNo; + + /** + * 是否支付成功 + */ + private Integer isPaySuccess; + + /** + * 支付成功的标记 + */ + private String successString; + + /** + * 支付金额 + */ + private Long payAmount; + + /** + * 回调内容 + */ + private String callbackContent; + + public Long getPayId() { + return payId; + } + + public void setPayId(Long payId) { + this.payId = payId; + } + + public String getBizPayNo() { + return bizPayNo; + } + + public void setBizPayNo(String bizPayNo) { + this.bizPayNo = bizPayNo; + } + + public Integer getIsPaySuccess() { + return isPaySuccess; + } + + public void setIsPaySuccess(Integer isPaySuccess) { + this.isPaySuccess = isPaySuccess; + } + + public String getSuccessString() { + return successString; + } + + public void setSuccessString(String successString) { + this.successString = successString; + } + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + public String getCallbackContent() { + return callbackContent; + } + + public void setCallbackContent(String callbackContent) { + this.callbackContent = callbackContent; + } + + @Override + public String toString() { + return "PayInfoResultBO{" + + "payId=" + payId + + ", bizPayNo='" + bizPayNo + '\'' + + ", isPaySuccess=" + isPaySuccess + + ", successString='" + successString + '\'' + + ", payAmount=" + payAmount + + ", callbackContent='" + callbackContent + '\'' + + '}'; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/RocketMqConfig.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/RocketMqConfig.java new file mode 100644 index 00000000..766fc055 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/RocketMqConfig.java @@ -0,0 +1,30 @@ +package com.mall4j.cloud.payment.config; + +import com.mall4j.cloud.common.rocketmq.config.RocketMqAdapter; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author FrozenWatermelon + * @date 2021/3/30 + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderNotifyTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.ORDER_NOTIFY_TOPIC); + } + + +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/SwaggerConfiguration.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/SwaggerConfiguration.java new file mode 100644 index 00000000..2f10f2e6 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.payment.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.payment.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/BackType.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/BackType.java new file mode 100644 index 00000000..ef94e518 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/BackType.java @@ -0,0 +1,43 @@ +package com.mall4j.cloud.payment.constant; + +/** + * 支付回调类型 + * @author FrozenWatermelon + */ +public enum BackType { + + /** + * api + */ + API(0), + /** + * 商家端 + */ + SHOP(1), + + /** + * 平台端 + */ + PLATFORM(2) + ; + + private Integer num; + + public Integer value() { + return num; + } + + BackType(Integer num) { + this.num = num; + } + + public static BackType instance(Integer value) { + BackType[] enums = values(); + for (BackType statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/PayStatus.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/PayStatus.java new file mode 100644 index 00000000..124072f6 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/constant/PayStatus.java @@ -0,0 +1,39 @@ +package com.mall4j.cloud.payment.constant; + +/** + * 支付状态 + * @author FrozenWatermelon + */ +public enum PayStatus { + + /** + * 未支付 + */ + UNPAY(0), + + /** + * 已支付 + */ + PAYED(1) + ; + + private Integer num; + + public Integer value() { + return num; + } + + PayStatus(Integer num) { + this.num = num; + } + + public static PayStatus instance(Integer value) { + PayStatus[] enums = values(); + for (PayStatus statusEnum : enums) { + if (statusEnum.value().equals(value)) { + return statusEnum; + } + } + return null; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayController.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayController.java new file mode 100644 index 00000000..6dbed59d --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayController.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.payment.controller; + + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.payment.bo.PayInfoBO; +import com.mall4j.cloud.payment.dto.PayInfoDTO; +import com.mall4j.cloud.payment.service.PayInfoService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + +/** + * @author FrozenWatermelon + */ +@RestController +@RequestMapping("/pay") +@Api(tags = "订单接口") +public class PayController { + + @Autowired + private PayInfoService payInfoService; + + @Autowired + private PayNoticeController payNoticeController; + + /** + * 支付接口 + */ + @PostMapping("/order") + @ApiOperation(value = "根据订单号进行支付", notes = "根据订单号进行支付") + public ServerResponseEntity pay(HttpServletRequest request, @Valid @RequestBody PayInfoDTO payParam) { + // 这里的地址是网关通过转发过来的时候,获取到当前服务器的地址,测试环境要用测试环境的uri + String gatewayUri = "http://192.168.1.17:8126/mall4cloud_payment"; + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + Long userId = userInfoInTokenBO.getUserId(); + PayInfoBO payInfo = payInfoService.pay(userId, payParam); + payInfo.setBizUserId(userInfoInTokenBO.getBizUserId()); + // 回调地址 + payInfo.setApiNoticeUrl(gatewayUri + "/notice/pay/order"); + payInfo.setReturnUrl(payParam.getReturnUrl()); + payNoticeController.submit(payInfo.getPayId()); + return ServerResponseEntity.success(payInfo.getPayId()); + } + + @GetMapping("/isPay/{orderIds}") + @ApiOperation(value = "根据订单号查询该订单是否已经支付", notes = "根据订单号查询该订单是否已经支付") + public ResponseEntity isPay(@PathVariable String orderIds) { + Long userId = AuthUserContext.get().getUserId(); + payInfoService.getPayStatusByOrderIds(orderIds); + Integer isPay = payInfoService.isPay(orderIds, userId); + return ResponseEntity.ok(BooleanUtil.isTrue(isPay)); + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayNoticeController.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayNoticeController.java new file mode 100644 index 00000000..10988cea --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/controller/PayNoticeController.java @@ -0,0 +1,46 @@ +package com.mall4j.cloud.payment.controller; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.payment.bo.PayInfoResultBO; +import com.mall4j.cloud.payment.model.PayInfo; +import com.mall4j.cloud.payment.service.PayInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author FrozenWatermelon + */ +@ApiIgnore +@RestController +@RequestMapping("/notice/pay") +public class PayNoticeController { + + @Autowired + private PayInfoService payInfoService; + + /** + * 支付异步回调 + */ + @RequestMapping("/order") + public ResponseEntity submit(Long payId) { + PayInfo payInfo = payInfoService.getByPayId(payId); + String[] orderIdStrArr = payInfo.getOrderIds().split(StrUtil.COMMA); + List orderIdList = new ArrayList<>(); + for (String s : orderIdStrArr) { + orderIdList.add(Long.valueOf(s)); + } + PayInfoResultBO payInfoResult = new PayInfoResultBO(); + payInfoResult.setPayId(payId); + payInfoResult.setBizPayNo(payInfo.getBizPayNo()); + payInfoResult.setCallbackContent(payInfo.getCallbackContent()); + // 支付成功 + payInfoService.paySuccess(payInfoResult,orderIdList); + return ResponseEntity.ok(""); + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/dto/PayInfoDTO.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/dto/PayInfoDTO.java new file mode 100644 index 00000000..f50ba887 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/dto/PayInfoDTO.java @@ -0,0 +1,49 @@ +package com.mall4j.cloud.payment.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.List; + +/** + * 订单支付记录DTO + * + * @author FrozenWatermelon + * @date 2020-12-25 09:50:59 + */ +public class PayInfoDTO{ + + @NotEmpty(message = "订单号不能为空") + @ApiModelProperty(value = "订单号", required = true) + private List orderIds; + + @ApiModelProperty(value = "支付完成回跳地址", required = true) + private String returnUrl; + + public List getOrderIds() { + return orderIds; + } + + public void setOrderIds(List orderIds) { + this.orderIds = orderIds; + } + + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + this.returnUrl = returnUrl; + } + + @Override + public String toString() { + return "PayInfoDTO{" + + "orderIds=" + orderIds + + ", returnUrl='" + returnUrl + '\'' + + '}'; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/mapper/PayInfoMapper.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/mapper/PayInfoMapper.java new file mode 100644 index 00000000..a877bb51 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/mapper/PayInfoMapper.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.payment.mapper; + +import com.mall4j.cloud.payment.model.PayInfo; +import org.apache.ibatis.annotations.Param; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-25 09:50:59 + */ +public interface PayInfoMapper { + + /** + * 根据订单支付记录id获取订单支付记录 + * + * @param payId 订单支付记录id + * @return 订单支付记录 + */ + PayInfo getByPayId(@Param("payId") Long payId); + + /** + * 保存订单支付记录 + * + * @param payInfo 订单支付记录 + */ + void save(@Param("payInfo") PayInfo payInfo); + + /** + * 更新订单支付记录 + * + * @param payInfo 订单支付记录 + */ + void update(@Param("payInfo") PayInfo payInfo); + + /** + * 根据订单支付记录id删除订单支付记录 + * + * @param payId + */ + void deleteById(@Param("payId") Long payId); + + /** + * 根据支付订单号获取订单支付状态 + * + * @param orderIds 订单号ids + * @return 支付状态 + */ + Integer getPayStatusByOrderIds(@Param("orderIds") String orderIds); + + /** + * 查询订单是否已经支付 + * @param orderIds 订单id + * @param userId 用户id + * @return 是否已经支付 + */ + Integer isPay(@Param("orderIds") String orderIds, @Param("userId") Long userId); +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/model/PayInfo.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/model/PayInfo.java new file mode 100644 index 00000000..c7692108 --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/model/PayInfo.java @@ -0,0 +1,177 @@ +package com.mall4j.cloud.payment.model; + +import java.io.Serializable; +import java.util.Date; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-25 09:50:59 + */ +public class PayInfo extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + public static final String DISTRIBUTED_ID_KEY = "mall4cloud-pay"; + + /** + * 支付单号 + */ + private Long payId; + + /** + * 用户id + */ + private Long userId; + + /** + * 外部订单流水号 + */ + private String bizPayNo; + + /** + * 本次支付关联的多个订单号 + */ + private String orderIds; + + /** + * 系统类型 见SysTypeEnum + */ + private Integer sysType; + + /** + * 支付状态 + */ + private Integer payStatus; + + /** + * 支付金额 + */ + private Long payAmount; + + /** + * 版本号 + */ + private Integer version; + + /** + * 回调内容 + */ + private String callbackContent; + + /** + * 回调时间 + */ + private Date callbackTime; + + /** + * 确认时间 + */ + private Date confirmTime; + + public Long getPayId() { + return payId; + } + + public void setPayId(Long payId) { + this.payId = payId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getBizPayNo() { + return bizPayNo; + } + + public void setBizPayNo(String bizPayNo) { + this.bizPayNo = bizPayNo; + } + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Integer getPayStatus() { + return payStatus; + } + + public void setPayStatus(Integer payStatus) { + this.payStatus = payStatus; + } + + public Long getPayAmount() { + return payAmount; + } + + public void setPayAmount(Long payAmount) { + this.payAmount = payAmount; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public String getCallbackContent() { + return callbackContent; + } + + public void setCallbackContent(String callbackContent) { + this.callbackContent = callbackContent; + } + + public Date getCallbackTime() { + return callbackTime; + } + + public void setCallbackTime(Date callbackTime) { + this.callbackTime = callbackTime; + } + + public Date getConfirmTime() { + return confirmTime; + } + + public void setConfirmTime(Date confirmTime) { + this.confirmTime = confirmTime; + } + + public String getOrderIds() { + return orderIds; + } + + public void setOrderIds(String orderIds) { + this.orderIds = orderIds; + } + + @Override + public String toString() { + return "PayInfo{" + + "payId=" + payId + + ", userId=" + userId + + ", bizPayNo='" + bizPayNo + '\'' + + ", orderIds='" + orderIds + '\'' + + ", sysType=" + sysType + + ", payStatus=" + payStatus + + ", payAmount=" + payAmount + + ", version=" + version + + ", callbackContent='" + callbackContent + '\'' + + ", callbackTime=" + callbackTime + + ", confirmTime=" + confirmTime + + '}'; + } +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/PayInfoService.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/PayInfoService.java new file mode 100644 index 00000000..451dd85c --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/PayInfoService.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.payment.service; + +import com.mall4j.cloud.payment.bo.PayInfoBO; +import com.mall4j.cloud.payment.bo.PayInfoResultBO; +import com.mall4j.cloud.payment.dto.PayInfoDTO; +import com.mall4j.cloud.payment.model.PayInfo; + +import java.util.List; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-25 09:50:59 + */ +public interface PayInfoService { + + /** + * 创建支付订单,返回给前端,前端唤起应用进行支付 + * @param userId 用户id + * @param payParam 支付参数 + * @return 前端唤起支付需要的参数 + */ + PayInfoBO pay(Long userId, PayInfoDTO payParam); + + /** + * 根据订单支付记录id获取订单支付记录 + * + * @param payId 订单支付记录id + * @return 订单支付记录 + */ + PayInfo getByPayId(Long payId); + + /** + * 标记为支付成功 + * @param payInfoResult 支付信息 + * @param orderIds 订单ids + */ + void paySuccess(PayInfoResultBO payInfoResult, List orderIds); + + /** + * 根据支付订单号获取订单支付状态 + * @param orderIds 订单号ids + * @return 支付状态 + */ + Integer getPayStatusByOrderIds(String orderIds); + + /** + * 查询订单是否已经支付 + * @param orderIds 订单id + * @param userId 用户id + * @return 是否已经支付 + */ + Integer isPay(String orderIds, Long userId); +} diff --git a/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/impl/PayInfoServiceImpl.java b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/impl/PayInfoServiceImpl.java new file mode 100644 index 00000000..4b93840b --- /dev/null +++ b/mall4cloud-payment/src/main/java/com/mall4j/cloud/payment/service/impl/PayInfoServiceImpl.java @@ -0,0 +1,120 @@ +package com.mall4j.cloud.payment.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.api.order.feign.OrderFeignClient; +import com.mall4j.cloud.api.order.vo.OrderAmountVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.order.bo.PayNotifyBO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.payment.bo.PayInfoBO; +import com.mall4j.cloud.payment.bo.PayInfoResultBO; +import com.mall4j.cloud.payment.constant.PayStatus; +import com.mall4j.cloud.payment.dto.PayInfoDTO; +import com.mall4j.cloud.payment.mapper.PayInfoMapper; +import com.mall4j.cloud.payment.model.PayInfo; +import com.mall4j.cloud.payment.service.PayInfoService; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * 订单支付记录 + * + * @author FrozenWatermelon + * @date 2020-12-25 09:50:59 + */ +@Service +public class PayInfoServiceImpl implements PayInfoService { + + @Autowired + private PayInfoMapper payInfoMapper; + + @Autowired + private SegmentFeignClient segmentFeignClient; + + @Autowired + private OrderFeignClient orderFeignClient; + + @Autowired + private RocketMQTemplate orderNotifyTemplate; + + @Override + @Transactional(rollbackFor = Exception.class) + public PayInfoBO pay(Long userId, PayInfoDTO payParam) { + // 支付单号 + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(PayInfo.DISTRIBUTED_ID_KEY); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + Long payId = segmentIdResponse.getData(); + List orderIds = payParam.getOrderIds(); + + ServerResponseEntity ordersAmountAndIfNoCancelResponse = orderFeignClient.getOrdersAmountAndIfNoCancel(orderIds); + // 如果订单已经关闭了,此时不能够支付了 + if (!ordersAmountAndIfNoCancelResponse.isSuccess()) { + throw new mall4cloudException(ordersAmountAndIfNoCancelResponse.getMsg()); + } + OrderAmountVO orderAmount = ordersAmountAndIfNoCancelResponse.getData(); + PayInfo payInfo = new PayInfo(); + payInfo.setPayId(payId); + payInfo.setUserId(userId); + payInfo.setPayAmount(orderAmount.getPayAmount()); + payInfo.setPayStatus(PayStatus.UNPAY.value()); + payInfo.setSysType(AuthUserContext.get().getSysType()); + payInfo.setVersion(0); + // 保存多个支付订单号 + payInfo.setOrderIds(StrUtil.join(StrUtil.COMMA, orderIds)); + // 保存预支付信息 + payInfoMapper.save(payInfo); + PayInfoBO payInfoDto = new PayInfoBO(); + payInfoDto.setBody("商城订单"); + payInfoDto.setPayAmount(orderAmount.getPayAmount()); + payInfoDto.setPayId(payId); + return payInfoDto; + } + + @Override + public PayInfo getByPayId(Long payId) { + return payInfoMapper.getByPayId(payId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void paySuccess(PayInfoResultBO payInfoResult, List orderIds) { + // 标记为支付成功状态 + PayInfo payInfo = new PayInfo(); + payInfo.setPayId(payInfoResult.getPayId()); + payInfo.setBizPayNo(payInfoResult.getBizPayNo()); + payInfo.setCallbackContent(payInfoResult.getCallbackContent()); + payInfo.setCallbackTime(new Date()); + payInfo.setPayStatus(PayStatus.PAYED.value()); + payInfoMapper.update(payInfo); + // 发送消息,订单支付成功 + SendStatus sendStatus = orderNotifyTemplate.syncSend(RocketMqConstant.ORDER_NOTIFY_TOPIC, new GenericMessage<>(new PayNotifyBO(orderIds))).getSendStatus(); + if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,因为订单回调会有多次,几乎不可能每次都无法发送出去,发的出去无所谓因为接口是幂等的 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + } + + @Override + public Integer getPayStatusByOrderIds(String orderIds) { + return payInfoMapper.getPayStatusByOrderIds(orderIds); + } + + @Override + public Integer isPay(String orderIds, Long userId) { + return payInfoMapper.isPay(orderIds, userId); + } +} diff --git a/mall4cloud-payment/src/main/resources/bootstrap.yml b/mall4cloud-payment/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..dc877e2d --- /dev/null +++ b/mall4cloud-payment/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9113 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-payment/src/main/resources/mapper/PayInfoMapper.xml b/mall4cloud-payment/src/main/resources/mapper/PayInfoMapper.xml new file mode 100644 index 00000000..bcbd674d --- /dev/null +++ b/mall4cloud-payment/src/main/resources/mapper/PayInfoMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + `pay_id`,`create_time`,`update_time`,`user_id`,`order_ids`,`biz_pay_no`,`sys_type`,`pay_status`, + `pay_amount`,`version`,`callback_content`,`callback_time`,`confirm_time` + + + + insert into pay_info (`pay_id`, `user_id`,`order_ids`,`biz_pay_no`,`sys_type`,`pay_status`, + `pay_amount`,`version`,`callback_content`,`callback_time`,`confirm_time`) + values (#{payInfo.payId},#{payInfo.userId},#{payInfo.orderIds},#{payInfo.bizPayNo},#{payInfo.sysType},#{payInfo.payStatus}, + #{payInfo.payAmount},#{payInfo.version},#{payInfo.callbackContent},#{payInfo.callbackTime},#{payInfo.confirmTime}); + + + update pay_info + + + `user_id` = #{payInfo.userId}, + + + `biz_pay_no` = #{payInfo.bizPayNo}, + + + `sys_type` = #{payInfo.sysType}, + + + `pay_status` = #{payInfo.payStatus}, + + + `pay_amount` = #{payInfo.payAmount}, + + + `version` = #{payInfo.version}, + + + `callback_content` = #{payInfo.callbackContent}, + + + `callback_time` = #{payInfo.callbackTime}, + + + `confirm_time` = #{payInfo.confirmTime}, + + + where pay_id = #{payInfo.payId} + + + delete from pay_info where pay_id = #{payId} + + + + + + diff --git a/mall4cloud-platform/pom.xml b/mall4cloud-platform/pom.xml new file mode 100644 index 00000000..bc6a6fdf --- /dev/null +++ b/mall4cloud-platform/pom.xml @@ -0,0 +1,75 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-platform + mall4cloud 平台端 + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-product + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-user + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-core + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/PlatformApplication.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/PlatformApplication.java new file mode 100644 index 00000000..6c6d81ea --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/PlatformApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.platform; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author lhd + * @date 2020/12/22 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class PlatformApplication { + + public static void main(String[] args) { + SpringApplication.run(PlatformApplication.class, args); + } + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/config/SwaggerConfiguration.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/config/SwaggerConfiguration.java new file mode 100644 index 00000000..7f4fcd71 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.platform.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author lhd + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.platform.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysConfigController.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysConfigController.java new file mode 100644 index 00000000..bc6d186e --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysConfigController.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.platform.controller; + +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.platform.model.SysConfig; +import com.mall4j.cloud.platform.service.SysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * @author chenlin + * @DateTime: 2020/3/19 8:29 + * @description: 支付配置、文件上传配置、短信配置、快递配置、小程序配置、公众号配置 + */ +@RestController +@RequestMapping("/sys_config") +public class SysConfigController { + + + @Autowired + private SysConfigService sysConfigService; + + /** + * 获取保存支付宝支付配置信息 + */ + @GetMapping("/info/{key}") + public ServerResponseEntity info(@PathVariable("key")String key){ + return ServerResponseEntity.success(sysConfigService.getValue(key)); + } + + /** + * 保存配置 + */ + @PostMapping("/save") + public ServerResponseEntity save(@RequestBody @Valid SysConfig sysConfig){ + sysConfigService.saveOrUpdateSysConfig(sysConfig); + return ServerResponseEntity.success(); + } + + + + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserAccountController.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserAccountController.java new file mode 100644 index 00000000..a0333eae --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserAccountController.java @@ -0,0 +1,65 @@ +package com.mall4j.cloud.platform.controller; + +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.platform.dto.ChangeAccountDTO; +import com.mall4j.cloud.platform.service.SysUserAccountService; +import com.mall4j.cloud.platform.service.SysUserService; +import com.mall4j.cloud.platform.vo.SysUserVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * @author lhd + * @date 2020/12/21 + */ +@RequestMapping(value = "/sys_user/account") +@RestController +@Api(tags = "平台用户账号信息") +public class SysUserAccountController { + + private final SysUserAccountService SysUserAccountService; + + private final SysUserService SysUserService; + + public SysUserAccountController(SysUserAccountService sysUserAccountService, SysUserService sysUserService) { + this.SysUserAccountService = sysUserAccountService; + this.SysUserService = sysUserService; + } + + + @GetMapping + @ApiOperation(value = "获取账号信息", notes = "获取账号信息") + public ServerResponseEntity getAccount(Long userId) { + return SysUserAccountService.getByUserIdAndSysType(userId, AuthUserContext.get().getSysType()); + } + + + @PostMapping + @ApiOperation(value = "添加账号", notes = "添加账号") + public ServerResponseEntity addAccount(@Valid @RequestBody ChangeAccountDTO changeAccountDTO) { + SysUserVO sysUserVO = SysUserService.getByUserId(changeAccountDTO.getUserId()); + if (sysUserVO == null) { + return ServerResponseEntity.showFailMsg("无法获取账户信息"); + } + if (Objects.equals(sysUserVO.getHasAccount(), 1)) { + return ServerResponseEntity.showFailMsg("已有账号,无需重复添加"); + } + return SysUserAccountService.save(changeAccountDTO); + } + + @PutMapping + @ApiOperation(value = "修改账号", notes = "修改账号") + public ServerResponseEntity updateAccount(@Valid @RequestBody ChangeAccountDTO changeAccountDTO) { + SysUserVO sysUserVO = SysUserService.getByUserId(changeAccountDTO.getUserId()); + if (sysUserVO == null || Objects.equals(sysUserVO.getHasAccount(), 0)) { + return ServerResponseEntity.showFailMsg("无法获取账户信息"); + } + return SysUserAccountService.update(changeAccountDTO); + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserController.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserController.java new file mode 100644 index 00000000..102e3385 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/controller/SysUserController.java @@ -0,0 +1,83 @@ +package com.mall4j.cloud.platform.controller; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.platform.dto.SysUserDTO; +import com.mall4j.cloud.platform.model.SysUser; +import com.mall4j.cloud.platform.service.SysUserService; +import com.mall4j.cloud.platform.vo.SysUserVO; +import com.mall4j.cloud.platform.vo.SysUserSimpleVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * @author lhd + * @date 2020/12/21 + */ +@RequestMapping(value = "/sys_user") +@RestController +@Api(tags = "平台用户信息") +public class SysUserController { + + @Autowired + private SysUserService sysUserService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/info") + @ApiOperation(value = "登陆平台用户信息", notes = "获取当前登陆平台用户的用户信息") + public ServerResponseEntity info() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + SysUserSimpleVO sysUserSimple = sysUserService.getSimpleByUserId(userInfoInTokenBO.getUserId()); + sysUserSimple.setIsAdmin(userInfoInTokenBO.getIsAdmin()); + return ServerResponseEntity.success(sysUserSimple); + } + + @GetMapping("/page") + @ApiOperation(value = "平台用户列表", notes = "获取平台用户列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, String nickName) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + PageVO sysUserPage = sysUserService.pageByShopId(pageDTO, userInfoInTokenBO.getTenantId(), nickName); + return ServerResponseEntity.success(sysUserPage); + } + + @GetMapping + @ApiOperation(value = "获取平台用户信息", notes = "根据用户id获取平台用户信息") + public ServerResponseEntity detail(@RequestParam Long sysUserId) { + return ServerResponseEntity.success(sysUserService.getByUserId(sysUserId)); + } + + @PostMapping + @ApiOperation(value = "保存平台用户信息", notes = "保存平台用户信息") + public ServerResponseEntity save(@Valid @RequestBody SysUserDTO sysUserDTO) { + SysUser sysUser = mapperFacade.map(sysUserDTO, SysUser.class); + sysUser.setSysUserId(null); + sysUser.setHasAccount(0); + sysUserService.save(sysUser,sysUserDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新平台用户信息", notes = "更新平台用户信息") + public ServerResponseEntity update(@Valid @RequestBody SysUserDTO sysUserDTO) { + SysUser sysUser = mapperFacade.map(sysUserDTO, SysUser.class); + sysUserService.update(sysUser,sysUserDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除平台用户信息", notes = "根据平台用户id删除平台用户信息") + public ServerResponseEntity delete(@RequestParam Long sysUserId) { + sysUserService.deleteById(sysUserId); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/ChangeAccountDTO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/ChangeAccountDTO.java new file mode 100644 index 00000000..625d2674 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/ChangeAccountDTO.java @@ -0,0 +1,71 @@ +package com.mall4j.cloud.platform.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author lhd + * @date 2020/12/22 + */ +public class ChangeAccountDTO { + + @NotNull(message = "userId not null") + @ApiModelProperty("用户id") + private Long userId; + + @NotBlank(message = "username not blank") + @ApiModelProperty("用户名") + private String username; + + @NotBlank(message = "password not blank") + @ApiModelProperty("密码") + private String password; + + @NotNull(message = "status not null") + @ApiModelProperty("状态 1启用 0禁用") + private Integer status; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "ChangeAccountDTO{" + + "userId=" + userId + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", status=" + status + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysConfigDTO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysConfigDTO.java new file mode 100644 index 00000000..47d9dea1 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysConfigDTO.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.platform.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; + +/** + * 系统配置信息表DTO + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +public class SysConfigDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("") + private Long id; + + @ApiModelProperty("key") + private String paramKey; + + @ApiModelProperty("value") + private String paramValue; + + @ApiModelProperty("备注") + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getParamKey() { + return paramKey; + } + + public void setParamKey(String paramKey) { + this.paramKey = paramKey; + } + + public String getParamValue() { + return paramValue; + } + + public void setParamValue(String paramValue) { + this.paramValue = paramValue; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + @Override + public String toString() { + return "SysConfigVO{" + + "id=" + id + + ",paramKey=" + paramKey + + ",paramValue=" + paramValue + + ",remark=" + remark + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysUserDTO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysUserDTO.java new file mode 100644 index 00000000..3426be44 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/dto/SysUserDTO.java @@ -0,0 +1,93 @@ +package com.mall4j.cloud.platform.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @author lhd + * @date 2020/9/8 + */ +public class SysUserDTO { + + @ApiModelProperty("平台用户id") + private Long sysUserId; + + @NotBlank(message = "昵称不能为空") + @ApiModelProperty("昵称") + private String nickName; + + @NotBlank(message = "头像不能为空") + @ApiModelProperty("头像") + private String avatar; + + @ApiModelProperty("员工编号") + private String code; + + @ApiModelProperty("联系方式") + private String phoneNum; + + @ApiModelProperty("角色id列表") + private List roleIds; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Long getSysUserId() { + return sysUserId; + } + + public void setSysUserId(Long sysUserId) { + this.sysUserId = sysUserId; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + @Override + public String toString() { + return "SysUserDTO{" + + "sysUserId=" + sysUserId + + ", nickName='" + nickName + '\'' + + ", avatar='" + avatar + '\'' + + ", code='" + code + '\'' + + ", phoneNum='" + phoneNum + '\'' + + ", roleIds=" + roleIds + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/feign/ConfigFeignController.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/feign/ConfigFeignController.java new file mode 100644 index 00000000..2a57c2b0 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/feign/ConfigFeignController.java @@ -0,0 +1,23 @@ +package com.mall4j.cloud.platform.feign; + +import com.mall4j.cloud.api.platform.feign.ConfigFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.platform.service.SysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author FrozenWatermelon + * @date 2020/12/30 + */ +@RestController +public class ConfigFeignController implements ConfigFeignClient { + + @Autowired + private SysConfigService sysConfigService; + + @Override + public ServerResponseEntity getConfig(String key) { + return ServerResponseEntity.success(sysConfigService.getValue(key)); + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysConfigMapper.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysConfigMapper.java new file mode 100644 index 00000000..3aa04032 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysConfigMapper.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.platform.mapper; + +import com.mall4j.cloud.platform.model.SysConfig; +import com.mall4j.cloud.platform.vo.SysConfigVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 系统配置信息表 + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +public interface SysConfigMapper { + + /** + * 获取系统配置信息表列表 + * @return 系统配置信息表列表 + */ + List list(); + + /** + * 根据系统配置信息表id获取系统配置信息表 + * + * @param id 系统配置信息表id + * @return 系统配置信息表 + */ + SysConfigVO getById(@Param("id") Long id); + + /** + * 保存系统配置信息表 + * @param sysConfig 系统配置信息表 + */ + void save(@Param("sysConfig") SysConfig sysConfig); + + /** + * 更新系统配置信息表 + * @param sysConfig 系统配置信息表 + */ + void update(@Param("sysConfig") SysConfig sysConfig); + + /** + * 根据系统配置key获取对应数量 + * @param paramKey key + * @return count + */ + int countByKey(@Param("paramKey") String paramKey); + + + /** + * 根据key,查询系统配置信息 + * @param key key + * @return SysConfig + */ + SysConfig queryByKey(@Param("key") String key); +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysUserMapper.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysUserMapper.java new file mode 100644 index 00000000..14aed08a --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/mapper/SysUserMapper.java @@ -0,0 +1,60 @@ +package com.mall4j.cloud.platform.mapper; + +import com.mall4j.cloud.platform.model.SysUser; +import com.mall4j.cloud.platform.vo.SysUserVO; +import com.mall4j.cloud.platform.vo.SysUserSimpleVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author lhd + * @date 2020/12/22 + */ +public interface SysUserMapper { + + /** + * 根据用户id获取当前登陆的商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + SysUserSimpleVO getSimpleByUserId(@Param("userId") Long userId); + + /** + * 获取平台用户列表 + * + * @param shopId 平台id + * @param nickName 昵称 + * @return 平台用户列表 + */ + List listByShopId(@Param("shopId") Long shopId, @Param("nickName") String nickName); + + /** + * 根据用户id获取商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + SysUserVO getByUserId(@Param("userId") Long userId); + + /** + * 保存商家用户信息 + * + * @param sysUser + */ + void save(@Param("sysUser") SysUser sysUser); + + /** + * 更新平台用户信息 + * + * @param sysUser + */ + void update(@Param("sysUser") SysUser sysUser); + + /** + * 根据平台用户id删除平台用户 + * @param sysUserId + */ + void deleteById(@Param("sysUserId") Long sysUserId); +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysConfig.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysConfig.java new file mode 100644 index 00000000..4adf7a3f --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysConfig.java @@ -0,0 +1,76 @@ +package com.mall4j.cloud.platform.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 系统配置信息表 + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +public class SysConfig extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * + */ + private Long id; + + /** + * key + */ + private String paramKey; + + /** + * value + */ + private String paramValue; + + /** + * 备注 + */ + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getParamKey() { + return paramKey; + } + + public void setParamKey(String paramKey) { + this.paramKey = paramKey; + } + + public String getParamValue() { + return paramValue; + } + + public void setParamValue(String paramValue) { + this.paramValue = paramValue; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + @Override + public String toString() { + return "SysConfigVO{" + + "id=" + id + + ",paramKey=" + paramKey + + ",paramValue=" + paramValue + + ",remark=" + remark + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysUser.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysUser.java new file mode 100644 index 00000000..d695272d --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/model/SysUser.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.platform.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.database.annotations.DistributedId; +import com.mall4j.cloud.common.model.BaseModel; +/** + * 平台用户 + * + * @author lhd + * @date 2020-12-21 14:16:34 + */ +public class SysUser extends BaseModel implements Serializable{ + + /** + * 平台用户id + */ + @DistributedId("mall4cloud-platform-user") + private Long sysUserId; + + /** + * 昵称 + */ + private String nickName; + + /** + * 头像 + */ + private String avatar; + + /** + * 员工编号 + */ + private String code; + + /** + * 联系方式 + */ + private String phoneNum; + + /** + * 是否已经设置账号 + */ + private Integer hasAccount; + + public Long getSysUserId() { + return sysUserId; + } + + public void setSysUserId(Long sysUserId) { + this.sysUserId = sysUserId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Integer getHasAccount() { + return hasAccount; + } + + public void setHasAccount(Integer hasAccount) { + this.hasAccount = hasAccount; + } + + @Override + public String toString() { + return "SysUserVO{" + + "sysUserId=" + sysUserId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",nickName=" + nickName + + ",avatar=" + avatar + + ",code=" + code + + ",phoneNum=" + phoneNum + + ",hasAccount=" + hasAccount + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysConfigService.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysConfigService.java new file mode 100644 index 00000000..56687ae2 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysConfigService.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.platform.service; + +import com.mall4j.cloud.platform.model.SysConfig; + +/** + * 系统配置信息表 + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +public interface SysConfigService { + /** + * 根据key,更新value + * @param key 参数key + * @param value 参数value + */ + void updateValueByKey(String key, String value); + + /** + * 删除配置信息 + * @param ids 配置项id列表 + */ + void deleteBatch(Long[] ids); + + /** + * 根据key,获取配置的value值 + * @param key 参数key + * @return value + */ + String getValue(String key); + + /** + * 根据key获取value + * @param key key + * @param clazz 泛型 + * @return 对象 + */ + T getSysConfigObject(String key, Class clazz); + + /** + * 删除配置 + * @param key key + */ + void removeSysConfig(String key); + + /** + * 保存or修改配置 + * @param sysConfig sysConfig + */ + void saveOrUpdateSysConfig(SysConfig sysConfig); + + /** + * 根据key获取配置对象 + * @param key key + * @return 配置信息 + */ + SysConfig getByKey(String key); +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserAccountService.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserAccountService.java new file mode 100644 index 00000000..261aa25e --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserAccountService.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.platform.service; + +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.platform.dto.ChangeAccountDTO; + +/** + * @author lhd + * @date 2020/12/22 + */ +public interface SysUserAccountService { + + /** + * 添加账户 + * @param changeAccountDTO 账户信息 + * @return void + */ + ServerResponseEntity save(ChangeAccountDTO changeAccountDTO); + + /** + * 更新账户 + * @param changeAccountDTO 账户信息 + * @return + */ + ServerResponseEntity update(ChangeAccountDTO changeAccountDTO); + + /** + * 根据用户id和系统类型获取用户信息 + * @param userId 用户id + * @param sysType 系统类型 + * @return void + */ + ServerResponseEntity getByUserIdAndSysType(Long userId, Integer sysType); +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserService.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserService.java new file mode 100644 index 00000000..2ea81f34 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/SysUserService.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.platform.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.platform.model.SysUser; +import com.mall4j.cloud.platform.vo.SysUserSimpleVO; +import com.mall4j.cloud.platform.vo.SysUserVO; + +import java.util.List; + +/** + * @author lhd + * @date 2020/12/22 + */ +public interface SysUserService { + + /** + * 根据用户id获取当前登陆的商家用户信息 + * @param userId 用户id + * @return 商家用户信息 + */ + SysUserSimpleVO getSimpleByUserId(Long userId); + + /** + * 分页获取平台用户列表 + * @param pageDTO 分页参数 + * @param shopId 平台id + * @param nickName 昵称 + * @return 平台用户列表 + */ + PageVO pageByShopId(PageDTO pageDTO, Long shopId, String nickName); + + /** + * 根据用户id获取商家用户信息 + * + * @param userId 用户id + * @return 商家用户信息 + */ + SysUserVO getByUserId(Long userId); + + /** + * 保存平台用户信息 + * @param sysUser 平台用户id + * @param roleIds 角色id列表 + */ + void save(SysUser sysUser, List roleIds); + + /** + * 更新平台用户信息 + * @param sysUser 平台用户id + * @param roleIds 角色id列表 + */ + void update(SysUser sysUser,List roleIds); + + /** + * 根据平台用户id删除平台用户信息 + * @param sysUserId 平台用户id + */ + void deleteById(Long sysUserId); + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysConfigServiceImpl.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysConfigServiceImpl.java new file mode 100644 index 00000000..0f95c33f --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.platform.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.cache.constant.ConfigCacheNames; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.platform.model.SysConfig; +import com.mall4j.cloud.platform.mapper.SysConfigMapper; +import com.mall4j.cloud.platform.service.SysConfigService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * 系统配置信息表 + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +@Service +public class SysConfigServiceImpl implements SysConfigService { + + @Resource + private SysConfigMapper sysConfigMapper; + + @Override + @Caching(evict = { + @CacheEvict(cacheNames= ConfigCacheNames.SYS_CONFIG_OBJECT,key="#key"), + @CacheEvict(cacheNames= ConfigCacheNames.SYS_CONFIG,key="#key") + }) + public void updateValueByKey(String key, String value) { +// sysConfigMapper.updateValueByKey(key, value); + } + + @Override + public void deleteBatch(Long[] ids) { +// sysConfigMapper.deleteBatch(ids); + } + + @Override + @Cacheable(cacheNames = ConfigCacheNames.SYS_CONFIG, key = "#key") + public String getValue(String key) { + SysConfig config = sysConfigMapper.queryByKey(key); + return config == null ? null : config.getParamValue(); + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = ConfigCacheNames.SYS_CONFIG_OBJECT,key="#key"), + @CacheEvict(cacheNames = ConfigCacheNames.SYS_CONFIG,key="#key") + }) + public void removeSysConfig(String key) { + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = ConfigCacheNames.SYS_CONFIG_OBJECT,key="#sysConfig.paramKey"), + @CacheEvict(cacheNames = ConfigCacheNames.SYS_CONFIG,key="#sysConfig.paramKey") + }) + public void saveOrUpdateSysConfig(SysConfig sysConfig) { + if (sysConfigMapper.countByKey(sysConfig.getParamKey()) > 0) { + sysConfigMapper.update(sysConfig); + }else{ + sysConfigMapper.save(sysConfig); + } + } + + @Override + @Cacheable(cacheNames = ConfigCacheNames.SYS_CONFIG, key = "#key") + public SysConfig getByKey(String key) { + return sysConfigMapper.queryByKey(key); + } + + + @Override + @Cacheable(cacheNames = ConfigCacheNames.SYS_CONFIG_OBJECT,key="#key") + public T getSysConfigObject(String key, Class clazz) { + String value = getValue(key); + if (StrUtil.isBlank(value)) { + return null; + } + + if(Objects.equals(String.class,clazz)){ + return (T)value; + }else{ + return Json.parseObject(value, clazz); + } + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserAccountServiceImpl.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserAccountServiceImpl.java new file mode 100644 index 00000000..576b8de8 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserAccountServiceImpl.java @@ -0,0 +1,82 @@ +package com.mall4j.cloud.platform.service.impl; + +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.IpHelper; +import com.mall4j.cloud.platform.dto.ChangeAccountDTO; +import com.mall4j.cloud.platform.mapper.SysUserMapper; +import com.mall4j.cloud.platform.model.SysUser; +import com.mall4j.cloud.platform.service.SysUserAccountService; +import io.seata.spring.annotation.GlobalTransactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; + +/** + * @author lhd + * @date 2020/12/22 + */ +@Service +public class SysUserAccountServiceImpl implements SysUserAccountService { + + @Resource + private SysUserMapper sysUserMapper; + @Autowired + private AccountFeignClient accountFeignClient; + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity save(ChangeAccountDTO changeAccountDTO) { + AuthAccountDTO authAccountDTO = getAuthAccountDTO(changeAccountDTO); + authAccountDTO.setCreateIp(IpHelper.getIpAddr()); + authAccountDTO.setIsAdmin(0); + // 保存 + ServerResponseEntity serverResponseEntity = accountFeignClient.save(authAccountDTO); + if (!serverResponseEntity.isSuccess()) { + return ServerResponseEntity.transform(serverResponseEntity); + } + SysUser sysUser = new SysUser(); + sysUser.setSysUserId(changeAccountDTO.getUserId()); + sysUser.setHasAccount(1); + sysUserMapper.update(sysUser); + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity update(ChangeAccountDTO changeAccountDTO) { + + AuthAccountDTO authAccountDTO = getAuthAccountDTO(changeAccountDTO); + // 更新,不涉及分布式事务 + ServerResponseEntity serverResponseEntity = accountFeignClient.update(authAccountDTO); + if (!serverResponseEntity.isSuccess()) { + return serverResponseEntity; + } + + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity getByUserIdAndSysType(Long userId, Integer sysType) { + return accountFeignClient.getByUserIdAndSysType(userId,sysType); + } + + private AuthAccountDTO getAuthAccountDTO(ChangeAccountDTO changeAccountDTO) { + AuthAccountDTO authAccountDTO = new AuthAccountDTO(); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + authAccountDTO.setPassword(changeAccountDTO.getPassword()); + authAccountDTO.setUsername(changeAccountDTO.getUsername()); + authAccountDTO.setStatus(changeAccountDTO.getStatus()); + authAccountDTO.setSysType(userInfoInTokenBO.getSysType()); + authAccountDTO.setTenantId(userInfoInTokenBO.getTenantId()); + authAccountDTO.setUserId(changeAccountDTO.getUserId()); + return authAccountDTO; + } + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserServiceImpl.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserServiceImpl.java new file mode 100644 index 00000000..b87dc8a0 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/service/impl/SysUserServiceImpl.java @@ -0,0 +1,94 @@ +package com.mall4j.cloud.platform.service.impl; + +import com.mall4j.cloud.platform.model.SysUser; +import com.mall4j.cloud.platform.vo.SysUserVO; +import org.springframework.cache.annotation.Cacheable; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.rbac.dto.UserRoleDTO; +import com.mall4j.cloud.api.rbac.feign.UserRoleFeignClient; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.platform.mapper.SysUserMapper; +import com.mall4j.cloud.platform.service.SysUserService; +import com.mall4j.cloud.platform.vo.SysUserSimpleVO; +import io.seata.spring.annotation.GlobalTransactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +/** + * @author lhd + * @date 2020/12/22 + */ +@Service +public class SysUserServiceImpl implements SysUserService { + + @Resource + private SysUserMapper sysUserMapper; + @Autowired + private AccountFeignClient accountFeignClient; + @Autowired + private UserRoleFeignClient userRoleFeignClient; + + + + @Override + @Cacheable(cacheNames = CacheNames.PLATFORM_SIMPLE_INFO_KEY, key = "#userId") + public SysUserSimpleVO getSimpleByUserId(Long userId) { + return sysUserMapper.getSimpleByUserId(userId); + } + + @Override + public PageVO pageByShopId(PageDTO pageDTO, Long shopId, String nickName) { + return PageUtil.doPage(pageDTO, () -> sysUserMapper.listByShopId(shopId, nickName)); + } + + @Override + public SysUserVO getByUserId(Long userId) { + SysUserVO sysUser = sysUserMapper.getByUserId(userId); + ServerResponseEntity> roleIds = userRoleFeignClient.getRoleIds(sysUser.getSysUserId()); + sysUser.setRoleIds(roleIds.getData()); + return sysUser; + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public void save(SysUser sysUser, List roleIds) { + UserRoleDTO userRoleDTO = new UserRoleDTO(); + userRoleDTO.setRoleIds(roleIds); + sysUserMapper.save(sysUser); + userRoleDTO.setUserId(sysUser.getSysUserId()); + userRoleFeignClient.saveByUserIdAndSysType(userRoleDTO); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.PLATFORM_SIMPLE_INFO_KEY, key = "#sysUser.sysUserId") + public void update(SysUser sysUser, List roleIds) { + UserRoleDTO userRoleDTO = new UserRoleDTO(); + userRoleDTO.setRoleIds(roleIds); + userRoleDTO.setUserId(sysUser.getSysUserId()); + sysUserMapper.update(sysUser); + userRoleFeignClient.updateByUserIdAndSysType(userRoleDTO); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.PLATFORM_SIMPLE_INFO_KEY, key = "#sysUserId") + public void deleteById(Long sysUserId) { + accountFeignClient.deleteByUserIdAndSysType(sysUserId); + userRoleFeignClient.deleteByUserIdAndSysType(sysUserId); + sysUserMapper.deleteById(sysUserId); + } + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysConfigVO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysConfigVO.java new file mode 100644 index 00000000..d1eb6968 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysConfigVO.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.platform.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 系统配置信息表VO + * + * @author lhd + * @date 2020-12-23 16:27:57 + */ +public class SysConfigVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("") + private Long id; + + @ApiModelProperty("key") + private String paramKey; + + @ApiModelProperty("value") + private String paramValue; + + @ApiModelProperty("备注") + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getParamKey() { + return paramKey; + } + + public void setParamKey(String paramKey) { + this.paramKey = paramKey; + } + + public String getParamValue() { + return paramValue; + } + + public void setParamValue(String paramValue) { + this.paramValue = paramValue; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + @Override + public String toString() { + return "SysConfigVO{" + + "id=" + id + + ",paramKey=" + paramKey + + ",paramValue=" + paramValue + + ",remark=" + remark + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserSimpleVO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserSimpleVO.java new file mode 100644 index 00000000..f034ba65 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserSimpleVO.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.platform.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author lhd + * @date 2020/9/2 + */ +public class SysUserSimpleVO { + + /** + * 昵称 + */ + @ApiModelProperty("昵称") + private String nickName; + + /** + * 头像 + */ + @ApiModelProperty("头像") + @JsonSerialize(using = ImgJsonSerializer.class) + private String avatar; + + private Integer isAdmin; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public Integer getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Integer isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "SysUserSimpleVO{" + + "nickName='" + nickName + '\'' + + ", avatar='" + avatar + '\'' + + ", isAdmin=" + isAdmin + + '}'; + } + +} diff --git a/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserVO.java b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserVO.java new file mode 100644 index 00000000..084adac9 --- /dev/null +++ b/mall4cloud-platform/src/main/java/com/mall4j/cloud/platform/vo/SysUserVO.java @@ -0,0 +1,132 @@ +package com.mall4j.cloud.platform.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * @author lhd + * @date 2020/9/2 + */ +public class SysUserVO { + + /** + * sysUserId + */ + @ApiModelProperty("平台用户id") + private Long sysUserId; + + /** + * 昵称 + */ + @ApiModelProperty("昵称") + private String nickName; + + /** + * 头像 + */ + @ApiModelProperty("头像") + @JsonSerialize(using = ImgJsonSerializer.class) + private String avatar; + + /** + * 员工编号 + */ + @ApiModelProperty("员工编号") + private String code; + + /** + * 联系方式 + */ + @ApiModelProperty("联系方式") + private String phoneNum; + + @ApiModelProperty("是否已经有账号了") + private Integer hasAccount; + + @ApiModelProperty("平台id") + private Long shopId; + + @ApiModelProperty("角色id列表") + private List roleIds; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public Long getSysUserId() { + return sysUserId; + } + + public void setSysUserId(Long sysUserId) { + this.sysUserId = sysUserId; + } + + public Integer getHasAccount() { + return hasAccount; + } + + public void setHasAccount(Integer hasAccount) { + this.hasAccount = hasAccount; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + @Override + public String toString() { + return "SysUserVO{" + + "sysUserId=" + sysUserId + + ", nickName='" + nickName + '\'' + + ", avatar='" + avatar + '\'' + + ", code='" + code + '\'' + + ", phoneNum='" + phoneNum + '\'' + + ", hasAccount=" + hasAccount + + ", shopId=" + shopId + + ", roleIds=" + roleIds + + '}'; + } +} diff --git a/mall4cloud-platform/src/main/resources/bootstrap.yml b/mall4cloud-platform/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..dcc69d90 --- /dev/null +++ b/mall4cloud-platform/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 9112 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ + diff --git a/mall4cloud-platform/src/main/resources/mapper/SysConfigMapper.xml b/mall4cloud-platform/src/main/resources/mapper/SysConfigMapper.xml new file mode 100644 index 00000000..1bb31f97 --- /dev/null +++ b/mall4cloud-platform/src/main/resources/mapper/SysConfigMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + `id`,`param_key`,`param_value`,`remark` + + + + + + + + insert into sys_config (`param_key`,`param_value`,`remark`) + values (#{sysConfig.paramKey},#{sysConfig.paramValue},#{sysConfig.remark}); + + + update sys_config + + + `param_key` = #{sysConfig.paramKey}, + + + `param_value` = #{sysConfig.paramValue}, + + + `remark` = #{sysConfig.remark}, + + + where param_key = #{sysConfig.paramKey} + + + delete from sys_config where param_key = #{paramKey} + + + + + + diff --git a/mall4cloud-platform/src/main/resources/mapper/SysUserMapper.xml b/mall4cloud-platform/src/main/resources/mapper/SysUserMapper.xml new file mode 100644 index 00000000..883bf58d --- /dev/null +++ b/mall4cloud-platform/src/main/resources/mapper/SysUserMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + sys_user_id, create_time, update_time, nick_name, avatar,`code`,phone_num,has_account + + + + + + + + insert into `sys_user` ( `sys_user_id`, `nick_name`, `avatar`, `code`, `phone_num`,has_account) + values (#{sysUser.sysUserId},#{sysUser.nickName},#{sysUser.avatar},#{sysUser.code},#{sysUser.phoneNum}, #{sysUser.hasAccount}); + + + update sys_user + + + nick_name = #{sysUser.nickName}, + + + avatar = #{sysUser.avatar}, + + + code = #{sysUser.code}, + + + phone_num = #{sysUser.phoneNum}, + + + has_account = #{sysUser.hasAccount}, + + + where sys_user_id = #{sysUser.sysUserId} + + + + delete from sys_user where sys_user_id = #{sysUserId} + + + + diff --git a/mall4cloud-product/pom.xml b/mall4cloud-product/pom.xml new file mode 100644 index 00000000..366a3373 --- /dev/null +++ b/mall4cloud-product/pom.xml @@ -0,0 +1,85 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-product + mall4cloud 商品端 + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-rocketmq + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-product + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-search + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-user + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/ProductApplication.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/ProductApplication.java new file mode 100644 index 00000000..175dce36 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/ProductApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.product; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/09/22 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class ProductApplication { + + public static void main(String[] args) { + SpringApplication.run(ProductApplication.class, args); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/bo/SkuWithStockBO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/bo/SkuWithStockBO.java new file mode 100644 index 00000000..387c6686 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/bo/SkuWithStockBO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.product.bo; + +/** + * @author FrozenWatermelon + * @date 2020/12/30 + */ +public class SkuWithStockBO { + + private Long id; + + private Long skuId; + + private Long spuId; + + private Integer count; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public String toString() { + return "SkuWithStockBO{" + + "id=" + id + + ", skuId=" + skuId + + ", spuId=" + spuId + + ", count=" + count + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/RocketMqConfig.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/RocketMqConfig.java new file mode 100644 index 00000000..caa0c0af --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/RocketMqConfig.java @@ -0,0 +1,29 @@ +package com.mall4j.cloud.product.config; + +import com.mall4j.cloud.common.rocketmq.config.RocketMqAdapter; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author FrozenWatermelon + * @date 2021/3/30 + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate stockMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.STOCK_UNLOCK_TOPIC); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/SwaggerConfiguration.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/SwaggerConfiguration.java new file mode 100644 index 00000000..b9660678 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.product.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.product.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/AttrType.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/AttrType.java new file mode 100644 index 00000000..dfcbc898 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/AttrType.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.product.constant; + +/** + * 属性类型 + * @author yxf + * @date 2020/11/20 + */ +public enum AttrType { + + /** + * 销售属性 + */ + SALES(0), + + /** + * 基本属性 + */ + BASIC(1) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + AttrType(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/SearchType.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/SearchType.java new file mode 100644 index 00000000..7730159e --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/constant/SearchType.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.product.constant; + +/** + * 属性的搜素类型 + * @author yxf + * @date 2020/11/20 + */ +public enum SearchType { + + /** + * 不需要作为搜索参数 + */ + NOT_SEARCH(0), + + /** + * 搜索参数 + */ + SEARCH(1) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + SearchType(Integer value) { + this.value = value; + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/AttrController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/AttrController.java new file mode 100644 index 00000000..cbdd0d48 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/AttrController.java @@ -0,0 +1,145 @@ +package com.mall4j.cloud.product.controller.admin; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.product.vo.AttrVO; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.constant.AttrType; +import com.mall4j.cloud.product.constant.SearchType; +import com.mall4j.cloud.product.dto.AttrDTO; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.product.model.AttrValue; +import com.mall4j.cloud.product.service.AttrService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +@RestController("platformAttrController") +@RequestMapping("/admin/attr") +@Api(tags = "admin-属性信息") +public class AttrController { + + @Autowired + private AttrService attrService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "获取属性信息列表", notes = "分页获取属性信息列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, AttrDTO attrDTO) { + PageVO attrPage = attrService.page(pageDTO, attrDTO); + return ServerResponseEntity.success(attrPage); + } + + @GetMapping + @ApiOperation(value = "获取属性信息", notes = "根据attrId获取属性信息") + public ServerResponseEntity getByAttrId(@RequestParam Long attrId) { + return ServerResponseEntity.success(attrService.getByAttrId(attrId)); + } + + @PostMapping + @ApiOperation(value = "保存属性信息", notes = "保存属性信息") + public ServerResponseEntity save(@Valid @RequestBody AttrDTO attrDTO) { + if (Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId()) && Objects.isNull(attrDTO.getAttrType())) { + throw new mall4cloudException("属性类型不能为空"); + } + checkAttrInfo(attrDTO); + Attr attr = mapperFacade.map(attrDTO, Attr.class); + attr.setAttrValues(mapperFacade.mapAsList(attrDTO.getAttrValues(), AttrValue.class)); + attrService.save(attr, attrDTO.getCategoryIds()); + removeCacheAttrUnionCategory(attrDTO.getCategoryIds()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新属性信息", notes = "更新属性信息") + public ServerResponseEntity update(@Valid @RequestBody AttrDTO attrDTO) { + checkAttrInfo(attrDTO); + Attr attr = mapperFacade.map(attrDTO, Attr.class); + if (CollUtil.isNotEmpty(attrDTO.getAttrValues())) { + attr.setAttrValues(mapperFacade.mapAsList(attrDTO.getAttrValues(), AttrValue.class)); + } + List categoryIds = null; + if (Objects.equals(AttrType.BASIC.value(), attr.getAttrType())) { + categoryIds = attrService.getAttrOfCategoryIdByAttrId(attrDTO.getAttrId()); + categoryIds.addAll(attrDTO.getCategoryIds()); + } + attrService.update(attr, attrDTO.getCategoryIds()); + removeCacheAttrUnionCategory(categoryIds); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除属性信息", notes = "根据属性信息id删除属性信息") + public ServerResponseEntity delete(@RequestParam Long attrId) { + List categoryIds = attrService.getAttrOfCategoryIdByAttrId(attrId); + attrService.deleteById(attrId); + if (CollUtil.isNotEmpty(categoryIds)) { + removeCacheAttrUnionCategory(categoryIds); + } + return ServerResponseEntity.success(); + } + + @GetMapping("/get_attrs_by_category_id") + @ApiOperation(value = "根据分类及属性类别获取属性列表", notes = "根据分类及属性类别获取属性列表") + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true) + public ServerResponseEntity> getAttrsByCategoryId(@RequestParam(value = "categoryId") Long categoryId) { + return ServerResponseEntity.success(attrService.getAttrsByCategoryIdAndAttrType(categoryId)); + } + + @GetMapping("/get_shop_attrs") + @ApiOperation(value = "获取店铺中的销售属性", notes = "获取店铺中的销售属性") + public ServerResponseEntity> getShopAttrs () { + return ServerResponseEntity.success(attrService.getShopAttrs(AuthUserContext.get().getTenantId())); + } + + /** + * 校验属性数据 + * @param attrDTO + */ + private void checkAttrInfo(AttrDTO attrDTO) { + if (!Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId())) { + attrDTO.setAttrType(AttrType.SALES.value()); + } + if (Objects.equals(AttrType.SALES.value(), attrDTO.getAttrType())) { + attrDTO.setSearchType(SearchType.NOT_SEARCH.value()); + return; + } + if (CollUtil.isEmpty(attrDTO.getCategoryIds())) { + throw new mall4cloudException("关联分类不能为空"); + } + if (Objects.isNull(attrDTO.getSearchType())) { + throw new mall4cloudException("搜索属性不能为空"); + } + } + + /** + * 删除属性关联的分类缓存 + */ + private void removeCacheAttrUnionCategory(List categoryIds) { + // 清除分类缓存 + if (!Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId()) || CollUtil.isEmpty(categoryIds)) { + return; + } + attrService.removeAttrByCategoryId(categoryIds); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/BrandController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/BrandController.java new file mode 100644 index 00000000..d432e994 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/BrandController.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.product.controller.admin; + +import com.mall4j.cloud.api.product.vo.BrandVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.BrandService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("adminBrandController") +@RequestMapping("/admin/brand") +@Api(tags = "admin-品牌信息") +public class BrandController { + + @Autowired + private BrandService brandService; + + @GetMapping("/get_brand_by_category_id") + @ApiImplicitParam(name = "categoryId",value = "分类id") + @ApiOperation(value = "根据分类,获取品牌列表", notes = "根据分类,获取品牌列表") + public ServerResponseEntity> getBrandByCategoryId(@RequestParam Long categoryId) { + return ServerResponseEntity.success(brandService.getBrandByCategoryId(categoryId)); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/CategoryController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/CategoryController.java new file mode 100644 index 00000000..d2664fc8 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/CategoryController.java @@ -0,0 +1,105 @@ +package com.mall4j.cloud.product.controller.admin; + +import com.mall4j.cloud.api.product.constant.CategoryLevel; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.dto.CategoryDTO; +import com.mall4j.cloud.product.model.Category; +import com.mall4j.cloud.product.service.CategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("platformCategoryController") +@RequestMapping("/admin/category") +@Api(tags = "admin-分类信息") +public class CategoryController { + + @Autowired + private CategoryService categoryService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping + @ApiOperation(value = "获取分类信息", notes = "根据categoryId获取分类信息") + public ServerResponseEntity getByCategoryId(@RequestParam Long categoryId) { + return ServerResponseEntity.success(categoryService.getById(categoryId)); + } + + @PostMapping + @ApiOperation(value = "保存分类信息", notes = "保存分类信息") + public ServerResponseEntity save(@Valid @RequestBody CategoryDTO categoryDTO) { + if (!Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId()) && categoryDTO.getLevel() > CategoryLevel.SECOND.value()) { + throw new mall4cloudException("分类等级最高只能为二级分类"); + } + Category category = mapperFacade.map(categoryDTO, Category.class); + categoryService.save(category); + categoryService.removeCategoryCache(AuthUserContext.get().getTenantId(), category.getParentId()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新分类信息", notes = "更新分类信息") + public ServerResponseEntity update(@Valid @RequestBody CategoryDTO categoryDTO) { + Category category = mapperFacade.map(categoryDTO, Category.class); + categoryService.update(category); + categoryService.removeCategoryCache(AuthUserContext.get().getTenantId(), category.getParentId()); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除分类信息", notes = "根据分类信息id删除分类信息") + public ServerResponseEntity delete(@RequestParam Long categoryId) { + CategoryVO categoryVO = categoryService.getById(categoryId); + categoryService.deleteById(categoryId); + categoryService.removeCategoryCache(AuthUserContext.get().getTenantId(), categoryVO.getParentId()); + return ServerResponseEntity.success(); + } + + @GetMapping("/platform_categories") + @ApiOperation(value = "获取平台所有的分类信息", notes = "获取所有的分类列表信息") + public ServerResponseEntity> platformCategories() { + return ServerResponseEntity.success(categoryService.list(Constant.PLATFORM_SHOP_ID)); + } + + @GetMapping("/shop_categories") + @ApiOperation(value = "获取店铺所有的分类信息", notes = "获取店铺所有的分类信息") + public ServerResponseEntity> shopCategories() { + return ServerResponseEntity.success(categoryService.list(AuthUserContext.get().getTenantId())); + } + + @GetMapping("/get_list_by_parent_id") + @ApiOperation(value = "根据上级id,获取分类列表信息", notes = "根据上级id,获取分类列表信息") + @ApiImplicitParams(value = { + @ApiImplicitParam(name = "parentId", value = "父类id") + }) + public ServerResponseEntity> getListByParentId(@RequestParam(value = "parentId") Long parentId) { + return ServerResponseEntity.success(categoryService.listByShopIdAndParenId(parentId, AuthUserContext.get().getTenantId())); + } + + @PutMapping(value = "/category_enable_or_disable") + @ApiOperation(value = "分类的启用或禁用", notes = "分类的启用或禁用") + public ServerResponseEntity categoryEnableOrDisable(@RequestBody CategoryDTO categoryDTO) { + Boolean isSuccess = categoryService.categoryEnableOrDisable(categoryDTO); + categoryService.removeCategoryCache(AuthUserContext.get().getTenantId(), null); + return ServerResponseEntity.success(isSuccess); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SkuStockLockController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SkuStockLockController.java new file mode 100644 index 00000000..f81e50d8 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SkuStockLockController.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.product.controller.admin; + +import com.mall4j.cloud.product.model.SkuStockLock; +import com.mall4j.cloud.product.service.SkuStockLockService; +import com.mall4j.cloud.product.dto.SkuStockLockDTO; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +@RestController("adminSkuStockLockController") +@RequestMapping("/admin/sku_stock_lock") +@Api(tags = "库存锁定信息") +public class SkuStockLockController { + + @Autowired + private SkuStockLockService skuStockLockService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "获取库存锁定信息列表", notes = "分页获取库存锁定信息列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO) { + PageVO skuStockLockPage = skuStockLockService.page(pageDTO); + return ServerResponseEntity.success(skuStockLockPage); + } + + @GetMapping + @ApiOperation(value = "获取库存锁定信息", notes = "根据id获取库存锁定信息") + public ServerResponseEntity getById(@RequestParam Long id) { + return ServerResponseEntity.success(skuStockLockService.getById(id)); + } + + @PostMapping + @ApiOperation(value = "保存库存锁定信息", notes = "保存库存锁定信息") + public ServerResponseEntity save(@Valid @RequestBody SkuStockLockDTO skuStockLockDTO) { + SkuStockLock skuStockLock = mapperFacade.map(skuStockLockDTO, SkuStockLock.class); + skuStockLock.setId(null); + skuStockLockService.save(skuStockLock); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新库存锁定信息", notes = "更新库存锁定信息") + public ServerResponseEntity update(@Valid @RequestBody SkuStockLockDTO skuStockLockDTO) { + SkuStockLock skuStockLock = mapperFacade.map(skuStockLockDTO, SkuStockLock.class); + skuStockLockService.update(skuStockLock); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除库存锁定信息", notes = "根据库存锁定信息id删除库存锁定信息") + public ServerResponseEntity delete(@RequestParam Long id) { + skuStockLockService.deleteById(id); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SpuController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SpuController.java new file mode 100644 index 00000000..7398b79c --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/admin/SpuController.java @@ -0,0 +1,269 @@ +package com.mall4j.cloud.product.controller.admin; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.dto.SpuPageSearchDTO; +import com.mall4j.cloud.product.model.SpuExtension; +import com.mall4j.cloud.product.service.*; +import com.mall4j.cloud.api.product.vo.*; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.*; +import java.util.stream.Collectors; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("platformSpuController") +@RequestMapping("/admin/spu") +@Api(tags = "admin-spu信息") +public class SpuController { + + @Autowired + private SpuService spuService; + @Autowired + private SkuService skuService; + @Autowired + private CategoryService categoryService; + @Autowired + private AttrService attrService; + @Autowired + private BrandService brandService; + + @GetMapping("/platform_page") + @ApiOperation(value = "获取平台spu信息列表", notes = "分页获取平台spu信息列表") + public ServerResponseEntity> platformPage(PageDTO pageDTO, SpuPageSearchDTO spuDTO) { + PageVO spuPage = spuService.platformPage(pageDTO, spuDTO); + return ServerResponseEntity.success(spuPage); + } + + @GetMapping("/page") + @ApiOperation(value = "获取spu信息列表", notes = "分页获取spu信息列表") + public ServerResponseEntity> page(PageDTO pageDTO, SpuPageSearchDTO spuDTO) { + PageVO spuPage = spuService.page(pageDTO, spuDTO); + return ServerResponseEntity.success(spuPage); + } + + @GetMapping + @ApiOperation(value = "获取spu信息", notes = "根据spuId获取spu信息") + public ServerResponseEntity getBySpuId(@RequestParam Long spuId) { + // 获取spu信息 + SpuVO spuVO = spuService.getBySpuId(spuId); + SpuExtension spuExtension = spuService.getSpuExtension(spuId); + spuVO.setTotalStock(spuExtension.getActualStock()); + spuVO.setSaleNum(spuExtension.getSaleNum()); + // 品牌信息 + spuVO.setBrand(brandService.getByBrandId(spuVO.getBrandId())); + // sku信息 + spuVO.setSkus(skuService.listBySpuIdAndExtendInfo(spuId)); + loadSpuAttrs(spuVO); + // 平台分类、店铺分类信息 + spuVO.setCategory(categoryService.getPathNameByCategoryId(spuVO.getCategoryId())); + spuVO.setShopCategory(categoryService.getPathNameByCategoryId(spuVO.getShopCategoryId())); + return ServerResponseEntity.success(spuVO); + } + + + @PostMapping + @ApiOperation(value = "保存spu信息", notes = "保存spu信息") + public ServerResponseEntity save(@Valid @RequestBody SpuDTO spuDTO) { + checkSaveOrUpdateInfo(spuDTO); + spuService.save(spuDTO); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新spu信息", notes = "更新spu信息") + public ServerResponseEntity update(@Valid @RequestBody SpuDTO spuDTO) { + checkSaveOrUpdateInfo(spuDTO); + List skuIds = spuDTO.getSkuList().stream().filter(sku -> Objects.nonNull(sku.getSkuId())).map(SkuDTO::getSkuId).collect(Collectors.toList()); + spuService.update(spuDTO); + // 清除缓存 + spuService.removeSpuCacheBySpuId(spuDTO.getSpuId()); + skuService.removeSkuCacheBySpuIdOrSkuIds(spuDTO.getSpuId(), skuIds); + return ServerResponseEntity.success(); + } + + + @DeleteMapping + @ApiOperation(value = "删除spu信息", notes = "根据spu信息id删除spu信息") + public ServerResponseEntity delete(@RequestParam Long spuId) { + spuService.deleteById(spuId); + // 清除缓存 + spuService.removeSpuCacheBySpuId(spuId); + skuService.removeSkuCacheBySpuIdOrSkuIds(spuId, null); + return ServerResponseEntity.success(); + } + + @PutMapping("/update_spu_data") + @ApiOperation(value = "修改spu(名称、价格、库存、序号)信息", notes = "更新spu信息") + public ServerResponseEntity updateSpuData(@RequestBody SpuDTO spuDTO) { + spuService.updateSpuOrSku(spuDTO); + // 清除缓存 + spuService.removeSpuCacheBySpuId(spuDTO.getSpuId()); + skuService.removeSkuCacheBySpuIdOrSkuIds(spuDTO.getSpuId(), null); + return ServerResponseEntity.success(); + } + + + /** + * 更新商品状态 + */ + @PutMapping("/prod_status") + @ApiOperation(value = "商品上下架", notes = "商品上下架") + public ServerResponseEntity spuChangeStatus(@RequestBody SpuDTO spuDTO) { + if (Objects.nonNull(spuDTO.getSpuId())) { + spuUpdateStatus(spuDTO); + } else if (CollUtil.isNotEmpty(spuDTO.getSpuIds())) { + spuBatchUpdateStatus(spuDTO); + } + return ServerResponseEntity.success(); + } + + /** + * spu上下架 + * @param spu + */ + private void spuUpdateStatus(SpuDTO spu) { + SpuVO dbSpu = spuService.getBySpuId(spu.getSpuId()); + String error = checkUpdateStatusData(dbSpu); + if (StrUtil.isNotBlank(error)) { + throw new mall4cloudException(error); + } + spuService.changeSpuStatus(spu.getSpuId(), spu.getStatus()); + spuService.removeSpuCacheBySpuId(spu.getSpuId()); + } + + /** + * spu批量上下架 + * @param spu + */ + private void spuBatchUpdateStatus(SpuDTO spu) { + List spuIds = new ArrayList<>(spu.getSpuIds()); + List errorList = new ArrayList<>(spu.getSpuIds()); + List spuList = spuService.listBySpuIds(spu.getSpuIds(), null, null); + if (CollUtil.isEmpty(spuList)) { + throw new mall4cloudException("您选择的商品信息有误,请刷新后重试"); + } + Map spuMap = spuList.stream().collect(Collectors.toMap(SpuVO::getSpuId, s -> s)); + for (Long spuId : spu.getSpuIds()) { + String errorInfo = checkUpdateStatusData(spuMap.get(spuId)); + if (StrUtil.isNotBlank(errorInfo)) { + spuIds.remove(spuId); + errorList.add(spuId); + } + } + if (CollUtil.isEmpty(spuIds)) { + throw new mall4cloudException("您所选择的商品中没有符合操作条件的商品"); + } + spuService.batchRemoveSpuCacheBySpuId(spuIds); + if (errorList.size() > 0) { + throw new mall4cloudException("商品id为:" + errorList.toString() + "的" + errorList.size() + "件商品不符合操作条件"); + } + spuService.changeSpuStatus(spu.getSpuId(), spu.getStatus()); + spuService.removeSpuCacheBySpuId(spu.getSpuId()); + } + + /** + * 加载spu属性列表 + * @param spuVO + */ + private void loadSpuAttrs(SpuVO spuVO) { + Map attrMap = null; + if (CollUtil.isNotEmpty(spuVO.getSpuAttrValues())) { + attrMap = spuVO.getSpuAttrValues().stream().collect(Collectors.toMap(SpuAttrValueVO::getAttrId, s -> s)); + } else { + attrMap = new HashMap<>(1); + } + List attrList = attrService.getAttrsByCategoryIdAndAttrType(spuVO.getCategoryId()); + List spuAttrValues = new ArrayList<>(); + for (AttrVO attrVO : attrList) { + SpuAttrValueVO spuAttrValueVO = attrMap.get(attrVO.getAttrId()); + SpuAttrValueVO newSpuAttrValue = new SpuAttrValueVO(); + if (Objects.nonNull(spuAttrValueVO)) { + Boolean hasValue = false; + for (AttrValueVO attrValue : attrVO.getAttrValues()) { + if (Objects.equals(attrValue.getAttrValueId(), spuAttrValueVO.getAttrValueId())) { + hasValue = true; + } + } + if (hasValue || CollUtil.isEmpty(attrVO.getAttrValues())) { + spuAttrValues.add(spuAttrValueVO); + continue; + } + newSpuAttrValue.setSpuAttrValueId(spuAttrValueVO.getSpuAttrValueId()); + } + newSpuAttrValue.setAttrId(attrVO.getAttrId()); + newSpuAttrValue.setAttrName(attrVO.getName()); + newSpuAttrValue.setSearchType(attrVO.getSearchType()); + spuAttrValues.add(newSpuAttrValue); + } + spuVO.setSpuAttrValues(spuAttrValues); + } + + /** + * 校验spu新增或更新信息 + * @param spuDTO + */ + private void checkSaveOrUpdateInfo(SpuDTO spuDTO) { + if (!Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId()) && Objects.isNull(spuDTO.getShopCategoryId())) { + throw new mall4cloudException("店铺分类不能为空"); + } + if (Objects.isNull(spuDTO.getSeq())) { + spuDTO.setSeq(0); + } + if (Objects.isNull(spuDTO.getBrandId())) { + spuDTO.setBrandId(0L); + } + } + + /** + * 校验spu上下架信息 + * @param spu + * @return + */ + private String checkUpdateStatusData(SpuVO spu) { + Long shopId = AuthUserContext.get().getTenantId(); + if (Objects.isNull(spu)) { + return "查找不到该商品信息"; + } + if (!Objects.equals(spu.getShopId(), shopId)) { + return "查找不到该商品信息"; + } + if (!(Objects.equals(spu.getStatus(), StatusEnum.ENABLE.value()) + || Objects.equals(spu.getStatus(), StatusEnum.DISABLE.value()))) { + return "商品状态异常,清刷新后重试"; + } + if(Objects.equals(spu.getStatus(),StatusEnum.ENABLE.value())){ + CategoryVO category = categoryService.getById(spu.getCategoryId()); + if (Objects.equals(category.getStatus(), StatusEnum.DISABLE.value())){ + return "该商品所属的平台分类处于下线中,商品不能上架,请联系管理员后再进行操作"; } + + if (Objects.equals(Constant.PLATFORM_SHOP_ID, AuthUserContext.get().getTenantId())) { + CategoryVO shopCategory = categoryService.getById(spu.getShopCategoryId()); + if (Objects.equals(category.getStatus(), StatusEnum.DISABLE.value())){ + return "该商品所属的店铺分类禁用中,商品不能进行上架操作"; + } + } + } + return null; + } + + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/AttrController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/AttrController.java new file mode 100644 index 00000000..e4ed0ec0 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/AttrController.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.product.controller.app; + +import com.mall4j.cloud.product.service.AttrService; +import com.mall4j.cloud.api.product.vo.AttrVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +@RestController("appAttrController") +@RequestMapping("/ua/attr") +@Api(tags = "app-属性信息") +public class AttrController { + + @Autowired + private AttrService attrService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping + @ApiOperation(value = "获取属性信息", notes = "根据attrId获取属性信息") + public ServerResponseEntity getByAttrId(@RequestParam Long attrId) { + return ServerResponseEntity.success(attrService.getByAttrId(attrId)); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/BrandController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/BrandController.java new file mode 100644 index 00000000..c99ada6b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/BrandController.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.product.controller.app; + +import com.mall4j.cloud.product.dto.BrandDTO; +import com.mall4j.cloud.product.service.BrandService; +import com.mall4j.cloud.api.product.vo.BrandVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.List; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("appBrandController") +@RequestMapping("/ua/brand") +@Api(tags = "app-品牌信息") +public class BrandController { + + @Autowired + private BrandService brandService; + + @GetMapping("/page") + @ApiOperation(value = "获取品牌信息列表", notes = "分页获取品牌信息列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, BrandDTO brandDTO) { + PageVO brandPage = brandService.page(pageDTO, brandDTO); + return ServerResponseEntity.success(brandPage); + } + + @GetMapping("/top_brand_list") + @ApiOperation(value = "置顶品牌列表", notes = "置顶品牌列表") + public ServerResponseEntity> topBrandList() { + List brandPage = brandService.topBrandList(); + return ServerResponseEntity.success(brandPage); + } + + @GetMapping("/list_by_category") + @ApiOperation(value = "分类-推荐品牌信息列表", notes = "分类-推荐品牌信息列表") + public ServerResponseEntity> getTopBrandList(Long categoryId) { + List brandPage = brandService.listByCategory(categoryId); + return ServerResponseEntity.success(brandPage); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/CategoryController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/CategoryController.java new file mode 100644 index 00000000..9ac91c11 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/CategoryController.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.product.controller.app; + +import com.mall4j.cloud.product.service.CategoryService; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("appCategoryController") +@RequestMapping("/ua/category") +@Api(tags = "app-分类信息") +public class CategoryController { + + @Autowired + private CategoryService categoryService; + + @GetMapping("/category_list") + @ApiOperation(value = "获取指定分类下的分类列表(顶级分类的parentId为0,默认为一级分类)", notes = "获取指定分类下的分类列表") + @ApiImplicitParams({ + @ApiImplicitParam(name = "parentId", value = "分类ID", dataType = "Long"), + @ApiImplicitParam(name = "shopId", value = "店铺id", dataType = "Long") + }) + public ServerResponseEntity> categoryList(@RequestParam(value = "parentId", defaultValue = "0") Long parentId, @RequestParam(value = "shopId", defaultValue = "0") Long shopId) { + List categories = categoryService.categoryList(shopId,parentId); + return ServerResponseEntity.success(categories); + } + + @GetMapping("/shop_category_list") + @ApiOperation(value = "店铺/平台的全部分类列表接口", notes = "店铺/平台分类列表接口") + @ApiImplicitParam(name = "shopId", value = "店铺id", required = false, dataType = "Long") + public ServerResponseEntity> shopCategoryList(@RequestParam(value = "shopId", defaultValue = "0") Long shopId) { + List categories = categoryService.shopCategoryList(shopId); + return ServerResponseEntity.success(categories); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/ShopCartController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/ShopCartController.java new file mode 100644 index 00000000..3bd532b8 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/ShopCartController.java @@ -0,0 +1,217 @@ +package com.mall4j.cloud.product.controller.app; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.api.product.manager.ShopCartAdapter; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.order.vo.ShopCartVO; +import com.mall4j.cloud.common.order.vo.ShopCartWithAmountVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.dto.shopcart.ChangeShopCartItemDTO; +import com.mall4j.cloud.product.dto.shopcart.CheckShopCartItemDTO; +import com.mall4j.cloud.product.model.ShopCartItem; +import com.mall4j.cloud.product.service.ShopCartService; +import com.mall4j.cloud.product.service.SkuService; +import com.mall4j.cloud.product.service.SpuService; +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.product.vo.ShopCartAmountVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * 购物车 + * + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +@RestController +@RequestMapping("/a/shop_cart") +@Api(tags = "app-购物车") +public class ShopCartController { + + @Autowired + private ShopCartService shopCartService; + + @Autowired + private MapperFacade mapperFacade; + + @Autowired + private SpuService spuService; + + @Autowired + private SkuService skuService; + + @Autowired + private ShopCartAdapter shopCartAdapter; + + /** + * 获取用户购物车信息 + * + * @return + */ + @GetMapping("/info") + @ApiOperation(value = "获取用户购物车信息", notes = "获取用户购物车信息") + public ServerResponseEntity info() { + // 拿到购物车的所有item + List shopCartItems = shopCartService.getShopCartItems(); + List shopCarts = shopCartAdapter.conversionShopCart(shopCartItems); + ShopCartWithAmountVO shopCartWithAmountVO = new ShopCartWithAmountVO(); + long total = 0L; + for (ShopCartItemVO shopCartItem : shopCartItems) { + if (Objects.equals(shopCartItem.getIsChecked(),1)){ + total += shopCartItem.getTotalAmount(); + } + } + shopCartWithAmountVO.setShopCarts(shopCarts); + shopCartWithAmountVO.setTotalMoney(total); + return ServerResponseEntity.success(shopCartWithAmountVO); + } + + /** + * 获取用户购物车信息 + * + * @return + */ + @GetMapping("/amount_info") + @ApiOperation(value = "获取用户购物车金额信息", notes = "获取用户购物车金额信息") + public ServerResponseEntity amountInfo() { + // 拿到购物车的所有item + List shopCartItems = shopCartService.getShopCartItems(); + List shopCarts = shopCartAdapter.conversionShopCart(shopCartItems); + ShopCartWithAmountVO shopCartWithAmountVO = new ShopCartWithAmountVO(); + shopCartWithAmountVO.setShopCarts(shopCarts); + + return ServerResponseEntity.success(mapperFacade.map(shopCartWithAmountVO, ShopCartAmountVO.class)); + } + + + + @DeleteMapping("/delete_item") + @ApiOperation(value = "删除用户购物车物品", notes = "通过购物车id删除用户购物车物品") + public ServerResponseEntity deleteItem(@RequestBody List shopCartItemIds) { + Long userId = AuthUserContext.get().getUserId(); + shopCartService.deleteShopCartItemsByShopCartItemIds(userId,shopCartItemIds); + return ServerResponseEntity.success(); + } + + @DeleteMapping("/delete_all") + @ApiOperation(value = "清空用户购物车所有物品", notes = "清空用户购物车所有物品") + public ServerResponseEntity deleteAll() { + Long userId = AuthUserContext.get().getUserId(); + shopCartService.deleteAllShopCartItems(userId); + // 删除成功 + return ServerResponseEntity.success(); + } + + @PostMapping("/check_items") + @ApiOperation(value = "勾选购物车", notes = "") + public ServerResponseEntity checkItems(@Valid @RequestBody List params) { + if(CollectionUtil.isEmpty(params)) { + return ServerResponseEntity.success(); + } + Long userId = AuthUserContext.get().getUserId(); + shopCartService.checkShopCartItems(userId,params); + return ServerResponseEntity.success(); + } + + + @PostMapping("/change_item") + @ApiOperation(value = "添加、修改用户购物车物品", notes = "通过商品id(prodId)、skuId、店铺Id(shopId),添加/修改用户购物车商品,并传入改变的商品个数(count)," + + "当count为正值时,增加商品数量,当count为负值时,将减去商品的数量,当最终count值小于0时,会将商品从购物车里面删除") + public ServerResponseEntity addItem(@Valid @RequestBody ChangeShopCartItemDTO param) { + + // 不用校验库存是否充足!!! + Long userId = AuthUserContext.get().getUserId(); + List shopCartItems = shopCartService.getShopCartItems(); + + SpuVO spu = spuService.getBySpuId(param.getSpuId()); + SkuVO sku = skuService.getSkuBySkuId(param.getSkuId()); + + + // 当商品状态不正常时,不能添加到购物车 + if (Objects.isNull(spu) || Objects.isNull(sku) || !Objects.equals(spu.getStatus(), StatusEnum.ENABLE.value()) || !Objects.equals(sku.getStatus(), StatusEnum.ENABLE.value()) || !Objects.equals(sku.getSpuId(),spu.getSpuId())) { + // 当返回商品不存在时,前端应该将商品从购物车界面移除 + return ServerResponseEntity.fail(ResponseEnum.SPU_NOT_EXIST); + } + // 保存shopId,不要让前端传过来 + param.setShopId(spu.getShopId()); + + + Integer oldCount = 0; + Long oldShopCartItemId = null; + for (ShopCartItemVO shopCartItemVo : shopCartItems) { + if (Objects.equals(param.getSkuId(), shopCartItemVo.getSkuId())) { + // 旧数量 + oldCount = shopCartItemVo.getCount(); + oldShopCartItemId = shopCartItemVo.getCartItemId(); + ShopCartItem shopCartItem = new ShopCartItem(); + shopCartItem.setUserId(userId); + shopCartItem.setCount(param.getCount() + shopCartItemVo.getCount()); + shopCartItem.setIsChecked(shopCartItemVo.getIsChecked()); + shopCartItem.setCartItemId(shopCartItemVo.getCartItemId()); + // 如果有个旧的sku,就说明是在切换sku + if (Objects.nonNull(param.getOldSkuId())) { + continue; + } + // 防止购物车变成负数,从购物车删除 + if (shopCartItem.getCount() <= 0) { + shopCartService.deleteShopCartItemsByShopCartItemIds(userId,Collections.singletonList(shopCartItem.getCartItemId())); + return ServerResponseEntity.success(); + } + shopCartService.updateShopCartItem(userId,shopCartItem); + return ServerResponseEntity.success(); + } + } + + if (Objects.nonNull(param.getOldSkuId())) { + for (ShopCartItemVO oldShopCartItem : shopCartItems) { + // 旧sku + if (Objects.equals(param.getOldSkuId(), oldShopCartItem.getSkuId())) { + ShopCartItem shopCartItem = new ShopCartItem(); + shopCartItem.setUserId(userId); + shopCartItem.setCartItemId(oldShopCartItem.getCartItemId()); + // 如果以前就存在这个商品,还要把以前的商品数量累加 + shopCartItem.setCount(param.getCount() + oldCount); + shopCartItem.setSkuId(param.getSkuId()); + + if (oldShopCartItemId != null) { + // 删除旧的购物项 + shopCartService.deleteShopCartItemsByShopCartItemIds(userId,Collections.singletonList(oldShopCartItemId)); + } + // 更新购物车 + shopCartService.updateShopCartItem(userId,shopCartItem); + return ServerResponseEntity.success(); + } + } + } + + // 所有都正常时 + shopCartService.addShopCartItem(userId,param,sku.getPriceFee()); + // 添加成功 + return ServerResponseEntity.success(); + } + + @GetMapping("/prod_count") + @ApiOperation(value = "获取购物车商品数量", notes = "获取购物车商品数量") + public ServerResponseEntity prodCount() { + return ServerResponseEntity.success(shopCartService.getShopCartItemCount(AuthUserContext.get().getUserId())); + } + + @GetMapping("/expiry_prod_list") + @ApiOperation(value = "获取购物车失效商品信息", notes = "获取购物车失效商品列表") + public ServerResponseEntity> expiryProdList() { + return ServerResponseEntity.success(shopCartService.getShopCartExpiryItems()); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SkuStockLockController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SkuStockLockController.java new file mode 100644 index 00000000..5853c5ca --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SkuStockLockController.java @@ -0,0 +1,69 @@ +package com.mall4j.cloud.product.controller.app; + +import com.mall4j.cloud.product.model.SkuStockLock; +import com.mall4j.cloud.product.service.SkuStockLockService; +import com.mall4j.cloud.product.dto.SkuStockLockDTO; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +@RestController("appSkuStockLockController") +@RequestMapping("/a/sku_stock_lock") +@Api(tags = "库存锁定信息") +public class SkuStockLockController { + + @Autowired + private SkuStockLockService skuStockLockService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "获取库存锁定信息列表", notes = "分页获取库存锁定信息列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO) { + PageVO skuStockLockPage = skuStockLockService.page(pageDTO); + return ServerResponseEntity.success(skuStockLockPage); + } + + @GetMapping + @ApiOperation(value = "获取库存锁定信息", notes = "根据id获取库存锁定信息") + public ServerResponseEntity getById(@RequestParam Long id) { + return ServerResponseEntity.success(skuStockLockService.getById(id)); + } + + @PostMapping + @ApiOperation(value = "保存库存锁定信息", notes = "保存库存锁定信息") + public ServerResponseEntity save(@Valid @RequestBody SkuStockLockDTO skuStockLockDTO) { + SkuStockLock skuStockLock = mapperFacade.map(skuStockLockDTO, SkuStockLock.class); + skuStockLockService.save(skuStockLock); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新库存锁定信息", notes = "更新库存锁定信息") + public ServerResponseEntity update(@Valid @RequestBody SkuStockLockDTO skuStockLockDTO) { + SkuStockLock skuStockLock = mapperFacade.map(skuStockLockDTO, SkuStockLock.class); + skuStockLockService.update(skuStockLock); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除库存锁定信息", notes = "根据库存锁定信息id删除库存锁定信息") + public ServerResponseEntity delete(@RequestParam Long id) { + skuStockLockService.deleteById(id); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SpuController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SpuController.java new file mode 100644 index 00000000..0f376ec9 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/app/SpuController.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.product.controller.app; + +import com.mall4j.cloud.product.vo.app.*; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.model.SpuExtension; +import com.mall4j.cloud.product.service.SkuService; +import com.mall4j.cloud.product.service.SpuService; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.product.vo.app.SkuAppVO; +import com.mall4j.cloud.product.vo.app.SpuAppVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("appSpuController") +@RequestMapping("/ua/spu") +@Api(tags = "app-spu信息") +public class SpuController { + + @Autowired + private SpuService spuService; + + @Autowired + private SkuService skuService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/prod_info") + @ApiOperation(value = "商品详情信息", notes = "根据商品ID(prodId)获取商品信息") + @ApiImplicitParam(name = "spuId", value = "商品ID", required = true, dataType = "Long") + public ServerResponseEntity prodInfo(@RequestParam("spuId") Long spuId) { + + SpuVO spu = spuService.getBySpuId(spuId); + SpuAppVO spuAppVO = mapperFacade.map(spu, SpuAppVO.class); + SpuExtension spuExtension = spuService.getSpuExtension(spuId); + spuAppVO.setTotalStock(spuExtension.getActualStock()); + spuAppVO.setSaleNum(spuExtension.getSaleNum()); + List skuAppVO = skuService.getSkuBySpuId(spu.getSpuId()); + spuAppVO.setSkus(skuAppVO); + return ServerResponseEntity.success(spuAppVO); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/platform/BrandController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/platform/BrandController.java new file mode 100644 index 00000000..acf05c87 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/controller/platform/BrandController.java @@ -0,0 +1,109 @@ +package com.mall4j.cloud.product.controller.platform; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.product.vo.BrandVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.dto.BrandDTO; +import com.mall4j.cloud.product.model.Brand; +import com.mall4j.cloud.product.service.BrandService; +import com.mall4j.cloud.product.service.CategoryBrandService; +import com.mall4j.cloud.product.service.CategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@RestController("platformBrandController") +@RequestMapping("/platform/brand") +@Api(tags = "platform-品牌信息") +public class BrandController { + + @Autowired + private BrandService brandService; + + @Autowired + private MapperFacade mapperFacade; + + @Autowired + private CategoryService categoryService; + + @Autowired + private CategoryBrandService categoryBrandService; + + @GetMapping("/page") + @ApiOperation(value = "获取品牌信息列表", notes = "分页获取品牌信息列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO, BrandDTO brandDTO) { + PageVO brandPage = brandService.page(pageDTO, brandDTO); + return ServerResponseEntity.success(brandPage); + } + + @GetMapping + @ApiOperation(value = "获取品牌信息", notes = "根据brandId获取品牌信息") + public ServerResponseEntity getByBrandId(@RequestParam Long brandId) { + BrandVO brand = brandService.getByBrandId(brandId); + categoryService.getPathNames(brand.getCategories()); + return ServerResponseEntity.success(brand); + } + + @PostMapping + @ApiOperation(value = "保存品牌信息", notes = "保存品牌信息") + public ServerResponseEntity save(@Valid @RequestBody BrandDTO brandDTO) { + if (CollUtil.isEmpty(brandDTO.getCategoryIds())) { + throw new mall4cloudException("分类不能为空"); + } + Brand brand = mapperFacade.map(brandDTO, Brand.class); + brandService.save(brand, brandDTO.getCategoryIds()); + brandService.removeCache(brandDTO.getCategoryIds()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新品牌信息", notes = "更新品牌信息") + public ServerResponseEntity update(@Valid @RequestBody BrandDTO brandDTO) { + if (CollUtil.isEmpty(brandDTO.getCategoryIds())) { + throw new mall4cloudException("分类不能为空"); + } + Brand brand = mapperFacade.map(brandDTO, Brand.class); + brandService.update(brand, brandDTO.getCategoryIds()); + // 清楚缓存 + List categoryIds = categoryBrandService.getCategoryIdBrandId(brand.getBrandId()); + categoryIds.addAll(brandDTO.getCategoryIds()); + brandService.removeCache(categoryIds); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除品牌信息", notes = "根据品牌信息id删除品牌信息") + public ServerResponseEntity delete(@RequestParam Long brandId) { + brandService.deleteById(brandId); + brandService.removeCache(categoryBrandService.getCategoryIdBrandId(brandId)); + return ServerResponseEntity.success(); + } + + @PutMapping(value = "/update_brand_status") + @ApiOperation(value = "更新品牌状态(启用或禁用)", notes = "更新品牌状态(启用或禁用)") + public ServerResponseEntity updateBrandStatus(@RequestBody BrandDTO brandDTO) { + if (Objects.isNull(brandDTO.getStatus())) { + throw new mall4cloudException("状态不能为空"); + } + if (Objects.isNull(brandDTO.getBrandId())) { + throw new mall4cloudException("品牌id不能为空"); + } + brandService.updateBrandStatus(brandDTO); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrCategoryDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrCategoryDTO.java new file mode 100644 index 00000000..cebb2f67 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrCategoryDTO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 属性与属性分组关联信息DTO + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +public class AttrCategoryDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性与分类关联id") + private Long attrCategoryId; + + @ApiModelProperty("分类id") + private Long categoryId; + + @ApiModelProperty("属性id") + private Long attrId; + + public Long getAttrCategoryId() { + return attrCategoryId; + } + + public void setAttrCategoryId(Long attrCategoryId) { + this.attrCategoryId = attrCategoryId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + @Override + public String toString() { + return "AttrCategoryDTO{" + + "attrCategoryId=" + attrCategoryId + + ",categoryId=" + categoryId + + ",attrId=" + attrId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrDTO.java new file mode 100644 index 00000000..0e8d02cf --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrDTO.java @@ -0,0 +1,119 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 属性信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +public class AttrDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("attr id") + private Long attrId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @NotNull(message = "属性名称不能为空") + @ApiModelProperty("属性名称") + private String name; + + @ApiModelProperty("属性描述") + private String desc; + + @ApiModelProperty("作为搜索参数 0:不需要,1:需要") + private Integer searchType; + + @ApiModelProperty("属性类型 0:销售属性,1:基本属性") + private Integer attrType; + + @ApiModelProperty("分类id列表") + private List categoryIds; + + @ApiModelProperty("属性值列表") + private List attrValues; + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Integer getAttrType() { + return attrType; + } + + public void setAttrType(Integer attrType) { + this.attrType = attrType; + } + + public Integer getSearchType() { + return searchType; + } + + public void setSearchType(Integer searchType) { + this.searchType = searchType; + } + + public List getAttrValues() { + return attrValues; + } + + public void setAttrValues(List attrValues) { + this.attrValues = attrValues; + } + + public List getCategoryIds() { + return categoryIds; + } + + public void setCategoryIds(List categoryIds) { + this.categoryIds = categoryIds; + } + + @Override + public String toString() { + return "AttrDTO{" + + "attrId=" + attrId + + ", shopId='" + shopId + '\'' + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", attrType=" + attrType + + ", searchType=" + searchType + + ", categoryIds=" + categoryIds + + ", attrValues=" + attrValues + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrValueDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrValueDTO.java new file mode 100644 index 00000000..c63b3fdb --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/AttrValueDTO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 属性值信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class AttrValueDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性id") + private Long attrValueId; + + @ApiModelProperty("属性ID") + private Long attrId; + + @ApiModelProperty("属性值") + private String value; + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "AttrValueDTO{" + + "attrValueId=" + attrValueId + + ",attrId=" + attrId + + ",value=" + value + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/BrandDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/BrandDTO.java new file mode 100644 index 00000000..4960acc4 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/BrandDTO.java @@ -0,0 +1,123 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 品牌信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class BrandDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("brand_id") + private Long brandId; + + @NotNull(message = "品牌名称不能为空") + @ApiModelProperty("品牌名称") + private String name; + + @ApiModelProperty("品牌描述") + private String desc; + + @NotNull(message = "logo图片不能为空") + @ApiModelProperty("品牌logo图片") + private String imgUrl; + + @NotNull(message = "首字母不能为空") + @ApiModelProperty("检索首字母") + private String firstLetter; + + @NotNull(message = "序号不能为空") + @ApiModelProperty("排序") + private Integer seq; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @NotNull(message = "分类不能为空") + @ApiModelProperty("分类") + private List categoryIds; + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public String getFirstLetter() { + return firstLetter; + } + + public void setFirstLetter(String firstLetter) { + this.firstLetter = firstLetter; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public List getCategoryIds() { + return categoryIds; + } + + public void setCategoryIds(List categoryIds) { + this.categoryIds = categoryIds; + } + + @Override + public String toString() { + return "BrandDTO{" + + "brandId=" + brandId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", firstLetter='" + firstLetter + '\'' + + ", seq=" + seq + + ", status=" + status + + ", categoryIds=" + categoryIds + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryBrandDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryBrandDTO.java new file mode 100644 index 00000000..543bd6af --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryBrandDTO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 品牌分类关联信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class CategoryBrandDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long id; + + @ApiModelProperty("品牌id") + private Long brandId; + + @ApiModelProperty("分类id") + private Long categoryId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + @Override + public String toString() { + return "CategoryBrandDTO{" + + "id=" + id + + ",brandId=" + brandId + + ",categoryId=" + categoryId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryDTO.java new file mode 100644 index 00000000..c2420328 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/CategoryDTO.java @@ -0,0 +1,156 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +/** + * 分类信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class CategoryDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("分类id") + private Long categoryId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @NotNull(message = "请选择上级分类") + @ApiModelProperty("父ID") + private Long parentId; + + @NotNull(message = "分类名称不能为空") + @ApiModelProperty("分类名称") + private String name; + + @ApiModelProperty("分类描述") + private String desc; + + @ApiModelProperty("分类地址{parent_id}-{child_id},...") + private String path; + + @NotNull(message = "状态不能为空") + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("分类图标") + private String icon; + + @ApiModelProperty("分类的显示图片") + private String imgUrl; + + @ApiModelProperty("分类层级 从0开始") + private Integer level; + + @ApiModelProperty("排序") + private Integer seq; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "CategoryDTO{" + + "categoryId=" + categoryId + + ", shopId=" + shopId + + ", parentId=" + parentId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", path='" + path + '\'' + + ", status=" + status + + ", icon='" + icon + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", level=" + level + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuDTO.java new file mode 100644 index 00000000..d0be7736 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuDTO.java @@ -0,0 +1,201 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * sku信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SkuDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("属性id") + private Long skuId; + + @ApiModelProperty("SPU id") + private Long spuId; + + @ApiModelProperty("多个销售属性值id逗号分隔") + private String attrs; + + @ApiModelProperty("sku名称") + private String skuName; + + @ApiModelProperty("banner图片") + private String imgUrl; + + @ApiModelProperty("售价,整数方式保存") + private Long priceFee; + + @ApiModelProperty("市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("库存") + private Integer stock; + + @ApiModelProperty("更新时,变化的库存") + private Integer changeStock; + + @ApiModelProperty("商品编码") + private String partyCode; + + @ApiModelProperty("商品条形码") + private String modelId; + + @ApiModelProperty("商品重量") + private Double weight; + + @ApiModelProperty("商品体积") + private Double volume; + + @ApiModelProperty("规格列表") + private List spuSkuAttrValues; + + public String getPartyCode() { + return partyCode; + } + + public void setPartyCode(String partyCode) { + this.partyCode = partyCode; + } + + public String getModelId() { + return modelId; + } + + public void setModelId(String modelId) { + this.modelId = modelId; + } + + public Double getWeight() { + return weight; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + public Double getVolume() { + return volume; + } + + public void setVolume(Double volume) { + this.volume = volume; + } + + public List getSpuSkuAttrValues() { + return spuSkuAttrValues; + } + + public void setSpuSkuAttrValues(List spuSkuAttrValues) { + this.spuSkuAttrValues = spuSkuAttrValues; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Integer getChangeStock() { + return changeStock; + } + + public void setChangeStock(Integer changeStock) { + this.changeStock = changeStock; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getAttrs() { + return attrs; + } + + public void setAttrs(String attrs) { + this.attrs = attrs; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + @Override + public String toString() { + return "SkuDTO{" + + "skuId=" + skuId + + ", spuId=" + spuId + + ", attrs='" + attrs + '\'' + + ", skuName='" + skuName + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", status=" + status + + ", stock=" + stock + + ", partyCode='" + partyCode + '\'' + + ", modelId='" + modelId + '\'' + + ", weight=" + weight + + ", volume=" + volume + + ", spuSkuAttrValues=" + spuSkuAttrValues + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockDTO.java new file mode 100644 index 00000000..bb502e33 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockDTO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 库存信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SkuStockDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("库存id") + private Long stockId; + + @ApiModelProperty("SKU ID") + private Long skuId; + + @ApiModelProperty("库存") + private Integer stock; + + public Long getStockId() { + return stockId; + } + + public void setStockId(Long stockId) { + this.stockId = stockId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "StockDTO{" + + "stockId=" + stockId + + ",skuId=" + skuId + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockLockDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockLockDTO.java new file mode 100644 index 00000000..bb7be27b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SkuStockLockDTO.java @@ -0,0 +1,91 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 库存锁定信息DTO + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +public class SkuStockLockDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("sku id") + private Long skuId; + + @ApiModelProperty("订单id") + private Long orderId; + + @ApiModelProperty("锁定库存数量") + private Integer count; + + @ApiModelProperty("状态-1已解锁 0待确定 1已锁定") + private Integer status; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SkuStockLockDTO{" + + "id=" + id + + ",spuId=" + spuId + + ",skuId=" + skuId + + ",orderId=" + orderId + + ",count=" + count + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuAttrValueDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuAttrValueDTO.java new file mode 100644 index 00000000..7d541193 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuAttrValueDTO.java @@ -0,0 +1,91 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品规格属性关联信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuAttrValueDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品属性值关联信息id") + private Long spuAttrValueId; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("规格属性id") + private Long attrId; + + @ApiModelProperty("规格属性名称") + private String attrName; + + @ApiModelProperty("规格属性值id") + private Long attrValueId; + + @ApiModelProperty("规格属性值名称") + private String attrValueName; + + public Long getSpuAttrValueId() { + return spuAttrValueId; + } + + public void setSpuAttrValueId(Long spuAttrValueId) { + this.spuAttrValueId = spuAttrValueId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + @Override + public String toString() { + return "SpuAttrValueDTO{" + + "spuAttrValueId=" + spuAttrValueId + + ",spuId=" + spuId + + ",attrId=" + attrId + + ",attrName=" + attrName + + ",attrValueId=" + attrValueId + + ",attrValueName=" + attrValueName + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDTO.java new file mode 100644 index 00000000..8b1a0a8b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDTO.java @@ -0,0 +1,294 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * spu信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("spuId") + private Long spuId; + + @ApiModelProperty("品牌ID") + private Long brandId; + + @NotNull(message = "分类不能为空") + @ApiModelProperty("分类ID") + private Long categoryId; + + @NotNull(message = "店铺分类不能为空") + @ApiModelProperty("店铺分类ID") + private Long shopCategoryId; + + @NotNull(message = "商品名称不能为空") + @ApiModelProperty("spu名称") + private String name; + + @ApiModelProperty("卖点") + private String sellingPoint; + + @NotNull(message = "商品轮播图不能为空") + @ApiModelProperty("商品介绍主图 多个图片逗号分隔") + private String imgUrls; + + @NotNull(message = "商品主图不能为空") + @ApiModelProperty("商品主图") + private String mainImgUrl; + + @ApiModelProperty("市场价") + private Long marketPriceFee; + + @NotNull(message = "售价不能为空") + @ApiModelProperty("售价") + private Long priceFee; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("商品属性值列表") + private List spuAttrValues; + + @NotEmpty(message = "sku信息不能为空") + @ApiModelProperty("商品规格列表") + private List skuList; + + @ApiModelProperty("商品详情") + private String detail; + + @NotNull(message = "总库存不能为空") + @ApiModelProperty("总库存") + private Integer totalStock; + + @ApiModelProperty("更新时,变化的库存") + private Integer changeStock; + + @ApiModelProperty("商品视频") + private String video; + + @ApiModelProperty("sku是否含有图片 0无 1有") + private Integer hasSkuImg; + + @ApiModelProperty("分组id") + private Long tagId; + + @ApiModelProperty("序号") + private Integer seq; + + @ApiModelProperty("spuId列表(商品上下架:批量操作时,用此参数)(批量处理参数)") + private List spuIds; + + @ApiModelProperty("店铺id") + private Long shopId; + + public Long getTagId() { + return tagId; + } + + public void setTagId(Long tagId) { + this.tagId = tagId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getVideo() { + return video; + } + + public void setVideo(String video) { + this.video = video; + } + + public List getSpuAttrValues() { + return spuAttrValues; + } + + public void setSpuAttrValues(List spuAttrValues) { + this.spuAttrValues = spuAttrValues; + } + + public List getSkuList() { + return skuList; + } + + public void setSkuList(List skuList) { + this.skuList = skuList; + } + + public Integer getTotalStock() { + return totalStock; + } + + public void setTotalStock(Integer totalStock) { + this.totalStock = totalStock; + } + + public Integer getChangeStock() { + return changeStock; + } + + public void setChangeStock(Integer changeStock) { + this.changeStock = changeStock; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopCategoryId() { + return shopCategoryId; + } + + public void setShopCategoryId(Long shopCategoryId) { + this.shopCategoryId = shopCategoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImgUrls() { + return imgUrls; + } + + public void setImgUrls(String imgUrls) { + this.imgUrls = imgUrls; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public Integer getHasSkuImg() { + return hasSkuImg; + } + + public void setHasSkuImg(Integer hasSkuImg) { + this.hasSkuImg = hasSkuImg; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public List getSpuIds() { + return spuIds; + } + + public void setSpuIds(List spuIds) { + this.spuIds = spuIds; + } + + @Override + public String toString() { + return "SpuDTO{" + + "spuId=" + spuId + + ", brandId=" + brandId + + ", categoryId=" + categoryId + + ", shopCategoryId=" + shopCategoryId + + ", name='" + name + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", imgUrls='" + imgUrls + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", marketPriceFee=" + marketPriceFee + + ", priceFee=" + priceFee + + ", status=" + status + + ", spuAttrValues=" + spuAttrValues + + ", skuList=" + skuList + + ", detail='" + detail + '\'' + + ", totalStock=" + totalStock + + ", changeStock=" + changeStock + + ", video='" + video + '\'' + + ", hasSkuImg=" + hasSkuImg + + ", tagId=" + tagId + + ", seq=" + seq + + ", spuIds=" + spuIds + + ", shopId=" + shopId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDetailDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDetailDTO.java new file mode 100644 index 00000000..5cf37689 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuDetailDTO.java @@ -0,0 +1,43 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品详情信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuDetailDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("商品详情") + private String detail; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + @Override + public String toString() { + return "SpuDetailDTO{" + + "spuId=" + spuId + + ",detail=" + detail + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuExtensionDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuExtensionDTO.java new file mode 100644 index 00000000..344af5d0 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuExtensionDTO.java @@ -0,0 +1,117 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * DTO + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public class SpuExtensionDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品扩展信息表id") + private Long spuExtendId; + + @ApiModelProperty("创建时间") + private Date createTime; + + @ApiModelProperty("更新时间") + private Date updateTime; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("销量") + private Integer saleNum; + + @ApiModelProperty("实际库存") + private Integer actualStock; + + @ApiModelProperty("锁定库存") + private Integer lockStock; + + @ApiModelProperty("可售卖库存") + private Integer stock; + + public Long getSpuExtendId() { + return spuExtendId; + } + + public void setSpuExtendId(Long spuExtendId) { + this.spuExtendId = spuExtendId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public Integer getActualStock() { + return actualStock; + } + + public void setActualStock(Integer actualStock) { + this.actualStock = actualStock; + } + + public Integer getLockStock() { + return lockStock; + } + + public void setLockStock(Integer lockStock) { + this.lockStock = lockStock; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "SpuExtensionVO{" + + "spuExtendId=" + spuExtendId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",saleNum=" + saleNum + + ",actualStock=" + actualStock + + ",lockStock=" + lockStock + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuPageSearchDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuPageSearchDTO.java new file mode 100644 index 00000000..10082243 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuPageSearchDTO.java @@ -0,0 +1,236 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * spu信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuPageSearchDTO { + + @ApiModelProperty("spuId") + private Long spuId; + + @ApiModelProperty("品牌ID") + private Long brandId; + + @ApiModelProperty("分类ID") + private Long categoryId; + + @ApiModelProperty("spu名称") + private String name; + + @ApiModelProperty("状态 1:enable, 0:disable, -1:deleted") + private Integer status; + + @ApiModelProperty("spuId列表(商品上下架:批量操作时,用此参数)(批量处理参数)") + private List spuIds; + + @ApiModelProperty("商品状态: 0.全部 1.销售中 2.已售罄 3.已下架 ") + private Integer spuStatus; + + @ApiModelProperty("最低价") + private Long minPrice; + + @ApiModelProperty("最高价") + private Long maxPrice; + + @ApiModelProperty("最低销量") + private Long minSaleNum; + + @ApiModelProperty("最高销量") + private Long maxSaleNum; + + @ApiModelProperty("商品编码") + private String partyCode; + + @ApiModelProperty("当前价排序 0:倒序 1:顺序") + private Integer priceFeeSort; + + @ApiModelProperty("市场价排序 0:倒序 1:顺序") + private Integer marketPriceFeeSort; + + @ApiModelProperty("销量排序 0:倒序 1:顺序") + private Integer saleNumSort; + + @ApiModelProperty("库存排序 0:倒序 1:顺序") + private Integer stockSort; + + @ApiModelProperty("序号排序 0:倒序 1:顺序") + private Integer seqSort; + + @ApiModelProperty("创建时间排序 0:倒序 1:顺序") + private Integer createTimeSort; + + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public List getSpuIds() { + return spuIds; + } + + public void setSpuIds(List spuIds) { + this.spuIds = spuIds; + } + + public Integer getSpuStatus() { + return spuStatus; + } + + public void setSpuStatus(Integer spuStatus) { + this.spuStatus = spuStatus; + } + + public Long getMinPrice() { + return minPrice; + } + + public void setMinPrice(Long minPrice) { + this.minPrice = minPrice; + } + + public Long getMaxPrice() { + return maxPrice; + } + + public void setMaxPrice(Long maxPrice) { + this.maxPrice = maxPrice; + } + + public Long getMinSaleNum() { + return minSaleNum; + } + + public void setMinSaleNum(Long minSaleNum) { + this.minSaleNum = minSaleNum; + } + + public Long getMaxSaleNum() { + return maxSaleNum; + } + + public void setMaxSaleNum(Long maxSaleNum) { + this.maxSaleNum = maxSaleNum; + } + + public String getPartyCode() { + return partyCode; + } + + public void setPartyCode(String partyCode) { + this.partyCode = partyCode; + } + + public Integer getPriceFeeSort() { + return priceFeeSort; + } + + public void setPriceFeeSort(Integer priceFeeSort) { + this.priceFeeSort = priceFeeSort; + } + + public Integer getMarketPriceFeeSort() { + return marketPriceFeeSort; + } + + public void setMarketPriceFeeSort(Integer marketPriceFeeSort) { + this.marketPriceFeeSort = marketPriceFeeSort; + } + + public Integer getSaleNumSort() { + return saleNumSort; + } + + public void setSaleNumSort(Integer saleNumSort) { + this.saleNumSort = saleNumSort; + } + + public Integer getStockSort() { + return stockSort; + } + + public void setStockSort(Integer stockSort) { + this.stockSort = stockSort; + } + + public Integer getSeqSort() { + return seqSort; + } + + public void setSeqSort(Integer seqSort) { + this.seqSort = seqSort; + } + + public Integer getCreateTimeSort() { + return createTimeSort; + } + + public void setCreateTimeSort(Integer createTimeSort) { + this.createTimeSort = createTimeSort; + } + + @Override + public String toString() { + return "SpuPageSearchDTO{" + + "spuId=" + spuId + + ", brandId=" + brandId + + ", categoryId=" + categoryId + + ", name='" + name + '\'' + + ", status=" + status + + ", spuIds=" + spuIds + + ", spuStatus=" + spuStatus + + ", minPrice=" + minPrice + + ", maxPrice=" + maxPrice + + ", minSaleNum=" + minSaleNum + + ", maxSaleNum=" + maxSaleNum + + ", partyCode='" + partyCode + '\'' + + ", priceFeeSort=" + priceFeeSort + + ", marketPriceFeeSort=" + marketPriceFeeSort + + ", saleNumSort=" + saleNumSort + + ", stockSort=" + stockSort + + ", seqSort=" + seqSort + + ", createTimeSort=" + createTimeSort + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuSkuAttrValueDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuSkuAttrValueDTO.java new file mode 100644 index 00000000..b779286e --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/SpuSkuAttrValueDTO.java @@ -0,0 +1,115 @@ +package com.mall4j.cloud.product.dto; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品sku销售属性关联信息DTO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuSkuAttrValueDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品sku销售属性关联信息id") + private Integer spuSkuAttrId; + + @ApiModelProperty("SPU ID") + private Long spuId; + + @ApiModelProperty("SKU ID") + private Long skuId; + + @ApiModelProperty("销售属性ID") + private Long attrId; + + @ApiModelProperty("销售属性名称") + private String attrName; + + @ApiModelProperty("销售属性值ID") + private Long attrValueId; + + @ApiModelProperty("销售属性值") + private String attrValueName; + + @ApiModelProperty("状态 1:enable, 0:disable") + private Integer status; + + public Integer getSpuSkuAttrId() { + return spuSkuAttrId; + } + + public void setSpuSkuAttrId(Integer spuSkuAttrId) { + this.spuSkuAttrId = spuSkuAttrId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SpuSkuAttrValueDTO{" + + "spuSkuAttrId=" + spuSkuAttrId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", attrId=" + attrId + + ", attrName='" + attrName + '\'' + + ", attrValueId=" + attrValueId + + ", attrValueName='" + attrValueName + '\'' + + ", status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/ChangeShopCartItemDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/ChangeShopCartItemDTO.java new file mode 100644 index 00000000..356ba660 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/ChangeShopCartItemDTO.java @@ -0,0 +1,105 @@ +package com.mall4j.cloud.product.dto.shopcart; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +public class ChangeShopCartItemDTO { + + @ApiModelProperty(value = "购物车ID", required = true) + private Long shopCartItemId; + + @NotNull(message = "商品ID不能为空") + @ApiModelProperty(value = "商品ID", required = true) + private Long spuId; + + @ApiModelProperty(value = "旧的skuId 如果传过来说明在变更sku", required = true) + private Long oldSkuId; + + @NotNull(message = "skuId不能为空") + @ApiModelProperty(value = "skuId", required = true) + private Long skuId; + + @ApiModelProperty(value = "店铺ID,前端不用传该字段") + private Long shopId; + + @NotNull(message = "商品个数不能为空") + @ApiModelProperty(value = "商品个数", required = true) + private Integer count; + + @ApiModelProperty(value = "商品是否勾选 true:勾选 ") + private Boolean isCheck; + + public Long getShopCartItemId() { + return shopCartItemId; + } + + public void setShopCartItemId(Long shopCartItemId) { + this.shopCartItemId = shopCartItemId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Boolean getCheck() { + return isCheck; + } + + public void setCheck(Boolean check) { + isCheck = check; + } + + public Long getOldSkuId() { + return oldSkuId; + } + + public void setOldSkuId(Long oldSkuId) { + this.oldSkuId = oldSkuId; + } + + @Override + public String toString() { + return "ChangeShopCartItemDTO{" + + "shopCartItemId=" + shopCartItemId + + ", spuId=" + spuId + + ", oldSkuId=" + oldSkuId + + ", skuId=" + skuId + + ", shopId=" + shopId + + ", count=" + count + + ", isCheck=" + isCheck + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/CheckShopCartItemDTO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/CheckShopCartItemDTO.java new file mode 100644 index 00000000..e94dd877 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/dto/shopcart/CheckShopCartItemDTO.java @@ -0,0 +1,44 @@ +package com.mall4j.cloud.product.dto.shopcart; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +/** + * @author FrozenWatermelon + * @date 2021-02-03 15:47:32 + */ +public class CheckShopCartItemDTO { + + @NotNull + @ApiModelProperty(value = "购物车ID", required = true) + private Long shopCartItemId; + + @NotNull + @ApiModelProperty(value = "商品是否勾选 1:勾选 0:未勾选") + private Integer isChecked; + + public Long getShopCartItemId() { + return shopCartItemId; + } + + public void setShopCartItemId(Long shopCartItemId) { + this.shopCartItemId = shopCartItemId; + } + + public Integer getIsChecked() { + return isChecked; + } + + public void setIsChecked(Integer isChecked) { + this.isChecked = isChecked; + } + + @Override + public String toString() { + return "CheckShopCartItemDTO{" + + "shopCartItemId=" + shopCartItemId + + ", isChecked=" + isChecked + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/CategoryFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/CategoryFeignController.java new file mode 100644 index 00000000..86156884 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/CategoryFeignController.java @@ -0,0 +1,34 @@ +package com.mall4j.cloud.product.feign; + +import com.mall4j.cloud.api.product.feign.CategoryFeignClient; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.CategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author lhd + * @date 2020/12/23 + */ +@RestController +public class CategoryFeignController implements CategoryFeignClient { + + @Autowired + private CategoryService categoryService; + + @Override + public ServerResponseEntity> listByOneLevel() { + return ServerResponseEntity.success(categoryService.listByShopIdAndParenId(Constant.PLATFORM_SHOP_ID, Constant.CATEGORY_ID)); + } + + @Override + public ServerResponseEntity> listCategoryId(Long categoryId) { + CategoryVO category = categoryService.getById(categoryId); + List categoryIds = categoryService.listCategoryId(category.getShopId(), category.getParentId()); + return ServerResponseEntity.success(categoryIds); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ProductFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ProductFeignController.java new file mode 100644 index 00000000..3a53cc41 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ProductFeignController.java @@ -0,0 +1,74 @@ +package com.mall4j.cloud.product.feign; + +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.api.multishop.feign.ShopDetailFeignClient; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.feign.ProductFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.SpuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/11/27 + */ +@RestController +public class ProductFeignController implements ProductFeignClient { + + @Autowired + private SpuService spuService; + @Autowired + private ShopDetailFeignClient shopDetailFeignClient; + + @Override + public ServerResponseEntity loadEsProductBO(Long spuId) { + EsProductBO esProductBO = spuService.loadEsProductBO(spuId); + // 获取店铺信息 + ServerResponseEntity shopDetailResponse = shopDetailFeignClient.getShopByShopId(esProductBO.getShopId()); + EsShopDetailBO shopDetail = shopDetailResponse.getData(); + esProductBO.setShopName(shopDetail.getShopName()); + esProductBO.setShopImg(shopDetail.getShopLogo()); + esProductBO.setShopType(shopDetail.getType()); + if (Objects.isNull(esProductBO.getSaleNum())) { + esProductBO.setSaleNum(0); + } + return ServerResponseEntity.success(esProductBO); + } + + @Override + public ServerResponseEntity> getSpuIdsByShopCategoryIds(List shopCategoryIds) { + return getSpuIdsBySpuUpdateDTO(shopCategoryIds, null, null, null); + } + + @Override + public ServerResponseEntity> getSpuIdsByCategoryIds(List categoryIds) { + return getSpuIdsBySpuUpdateDTO(null, categoryIds, null, null); + } + + @Override + public ServerResponseEntity> getSpuIdsByBrandId(Long brandId) { + return getSpuIdsBySpuUpdateDTO(null, null, brandId, null); + } + + @Override + public ServerResponseEntity> getSpuIdsByShopId(Long shopId) { + return getSpuIdsBySpuUpdateDTO(null, null, null, shopId); + } + + /** + * 获取spuId列表 + * @param shopCategoryIds 店铺分类id列表 + * @param categoryIds 平台分类Id列表 + * @param brandId 品牌id + * @param shopId 店铺id + * @return + */ + public ServerResponseEntity> getSpuIdsBySpuUpdateDTO(List shopCategoryIds, List categoryIds, Long brandId, Long shopId) { + List spuIds = spuService.getSpuIdsBySpuUpdateDTO(shopCategoryIds, categoryIds, brandId, shopId); + return ServerResponseEntity.success(spuIds); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ShopCartFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ShopCartFeignController.java new file mode 100644 index 00000000..d2ab6ed9 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/ShopCartFeignController.java @@ -0,0 +1,41 @@ +package com.mall4j.cloud.product.feign; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.api.product.feign.ShopCartFeignClient; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.service.ShopCartService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +@RestController +public class ShopCartFeignController implements ShopCartFeignClient { + + @Autowired + private ShopCartService shopCartService; + + @Override + public ServerResponseEntity> getCheckedShopCartItems() { + List checkedShopCartItems = shopCartService.getCheckedShopCartItems(); + if (CollectionUtil.isNotEmpty(checkedShopCartItems)) { + for (ShopCartItemVO shopCartItem : checkedShopCartItems) { + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + } + } + return ServerResponseEntity.success(checkedShopCartItems); + } + + @Override + public ServerResponseEntity deleteItem(List shopCartItemIds) { + Long userId = AuthUserContext.get().getUserId(); + shopCartService.deleteShopCartItemsByShopCartItemIds(userId,shopCartItemIds); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuFeignController.java new file mode 100644 index 00000000..b6b5ac1a --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuFeignController.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.product.feign; + +import com.mall4j.cloud.api.product.feign.SkuFeignClient; +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.SkuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +@RestController +public class SkuFeignController implements SkuFeignClient { + + @Autowired + private SkuService skuService; + + + @Override + public ServerResponseEntity getById(Long skuId) { + return ServerResponseEntity.success(skuService.getSkuBySkuId(skuId)); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuStockLockFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuStockLockFeignController.java new file mode 100644 index 00000000..58092640 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SkuStockLockFeignController.java @@ -0,0 +1,28 @@ +package com.mall4j.cloud.product.feign; + +import com.mall4j.cloud.api.product.dto.SkuStockLockDTO; +import com.mall4j.cloud.api.product.feign.SkuStockLockFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.*; +import com.mall4j.cloud.product.service.SkuStockLockService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +@RestController +public class SkuStockLockFeignController implements SkuStockLockFeignClient { + + + @Autowired + private SkuStockLockService skuStockLockService; + + @Override + public ServerResponseEntity lock(List skuStockLocksParam) { + return skuStockLockService.lock(skuStockLocksParam); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SpuFeignController.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SpuFeignController.java new file mode 100644 index 00000000..5bd08b0a --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/feign/SpuFeignController.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.product.feign; + +import com.mall4j.cloud.api.product.feign.SpuFeignClient; +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.api.product.vo.SpuAndSkuVO; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.service.SkuService; +import com.mall4j.cloud.product.service.SpuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/12/8 + */ +@RestController +public class SpuFeignController implements SpuFeignClient { + + @Autowired + private SpuService spuService; + + @Autowired + private SkuService skuService; + + @Override + public ServerResponseEntity getById(Long spuId) { + SpuVO spuVO = spuService.getBySpuId(spuId); + SpuVO spu = new SpuVO(); + spu.setSpuId(spuVO.getSpuId()); + spu.setName(spuVO.getName()); + spu.setMainImgUrl(spuVO.getMainImgUrl()); + return ServerResponseEntity.success(spu); + } + + @Override + public ServerResponseEntity getSpuAndSkuById(Long spuId, Long skuId) { + SpuVO spu = spuService.getBySpuId(spuId); + SkuVO sku = skuService.getSkuBySkuId(skuId); + + // 当商品状态不正常时,不能添加到购物车 + boolean spuIsNotExist = Objects.isNull(spu) || Objects.isNull(sku) || !Objects.equals(spu.getStatus(), StatusEnum.ENABLE.value()) || !Objects.equals(sku.getStatus(), StatusEnum.ENABLE.value()) || !Objects.equals(sku.getSpuId(), spu.getSpuId()); + if (spuIsNotExist) { + // 当返回商品不存在时,前端应该将商品从购物车界面移除 + return ServerResponseEntity.fail(ResponseEnum.SPU_NOT_EXIST); + } + + SpuAndSkuVO spuAndSku = new SpuAndSkuVO(); + spuAndSku.setSku(sku); + spuAndSku.setSpu(spu); + return ServerResponseEntity.success(spuAndSku); + } + + @Override + public ServerResponseEntity> getSpusBySpuIds(List spuIds, String spuName, Integer isFailure) { + return ServerResponseEntity.success(spuService.listBySpuIds(spuIds,spuName,isFailure)); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/OrderNotifyStockConsumer.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/OrderNotifyStockConsumer.java new file mode 100644 index 00000000..791a12fa --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/OrderNotifyStockConsumer.java @@ -0,0 +1,29 @@ +package com.mall4j.cloud.product.listener; + +import com.mall4j.cloud.common.order.bo.PayNotifyBO; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.product.service.SkuStockLockService; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 解锁库存的监听 + * @author FrozenWatermelon + */ +@Component +@RocketMQMessageListener(topic = RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC,consumerGroup = RocketMqConstant.ORDER_NOTIFY_STOCK_TOPIC) +public class OrderNotifyStockConsumer implements RocketMQListener { + + @Autowired + private SkuStockLockService skuStockLockService; + + /** + * 订单支付成功锁定库存 + */ + @Override + public void onMessage(PayNotifyBO message) { + skuStockLockService.markerStockUse(message.getOrderIds()); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/StockUnlockConsumer.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/StockUnlockConsumer.java new file mode 100644 index 00000000..7cd9eb08 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/listener/StockUnlockConsumer.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.product.listener; + +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.product.service.SkuStockLockService; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 解锁库存的监听 + * @author FrozenWatermelon + */ +@Component +@RocketMQMessageListener(topic = RocketMqConstant.STOCK_UNLOCK_TOPIC,consumerGroup = RocketMqConstant.STOCK_UNLOCK_TOPIC) +public class StockUnlockConsumer implements RocketMQListener> { + + @Autowired + private SkuStockLockService skuStockLockService; + + /** + * 1、库存锁定一定时间后,如果订单支付未支付,则解锁库存(有可能库存锁定成功,订单因为异常回滚导致订单未创建) + * 2、取消订单,直接解锁库存 + */ + @Override + public void onMessage(List orderIds) { + skuStockLockService.unlockStock(orderIds); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrCategoryMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrCategoryMapper.java new file mode 100644 index 00000000..8d1b36fb --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrCategoryMapper.java @@ -0,0 +1,47 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.api.product.vo.CategoryVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 属性与属性分组关联信息 + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +public interface AttrCategoryMapper { + + /** + * 根据属性id,获取属性关联的分类id列表 + * + * @param attrId 属性id + * @return 分类id列表 + */ + List getCategoryIdsByAttrId(@Param("attrId") Long attrId); + + /** + * 批量保存属性与属性分组关联信息 + * + * @param attrId 属性id + * @param categoryIds 分类id列表 + */ + void saveBatch(@Param("attrId") Long attrId, @Param("categoryIds") List categoryIds); + + /** + * 批量删除属性与分类关联信息 + * + * @param attrId 属性与分类关联id列表 + * @param categoryIds 属性与分类关联id列表 + */ + void deleteBatch(@Param("attrId") Long attrId, @Param("categoryIds") List categoryIds); + + /** + * 据属性Id,获取属性关联的分类列表信息 + * + * @param attrId + * @return + */ + List listByAttrId(@Param("attrId") Long attrId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrMapper.java new file mode 100644 index 00000000..2de402fc --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrMapper.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.common.database.util.PageAdapter; +import com.mall4j.cloud.product.dto.AttrDTO; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.api.product.vo.AttrVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +public interface AttrMapper { + + /** + * 获取属性信息列表 + * + * @param pageAdapter 分页参数 + * @param attrDTO 属性数据 + * @return 属性信息列表 + */ + List list(@Param("page") PageAdapter pageAdapter, @Param("attr") AttrDTO attrDTO); + /** + * 获取属性总数 + * + * @param attrDTO + * @return + */ + Long countAttr(@Param("attr") AttrDTO attrDTO); + + /** + * 根据属性信息id获取属性信息 + * + * @param attrId 属性信息id + * @return 属性信息 + */ + AttrVO getByAttrId(@Param("attrId") Long attrId); + + /** + * 保存属性信息 + * + * @param attr 属性信息 + */ + void save(@Param("attr") Attr attr); + + /** + * 更新属性信息 + * + * @param attr 属性信息 + */ + void update(@Param("attr") Attr attr); + + /** + * 根据属性信息id删除属性信息 + * + * @param attrId + */ + void deleteById(@Param("attrId") Long attrId); + + /** + * 根据分类和属性类型,获取对应的属性列表 + * + * @param categoryId + * @return + */ + List getAttrsByCategoryIdAndAttrType(@Param("categoryId") Long categoryId); + + /** + * 获取店铺中的销售属性 + * @param shopId + * @return 销售属性列表 + */ + List getShopAttrs(Long shopId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrValueMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrValueMapper.java new file mode 100644 index 00000000..af2c6014 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/AttrValueMapper.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.AttrValue; +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.product.model.SpuSkuAttrValue; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 属性值信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface AttrValueMapper { + + /** + * 批量保存属性值 + * @param attrValues + */ + void saveBatch(@Param("attrValues") List attrValues); + + /** + * 根据 attrId 获取属性值id列表 + * @param attrId + * @return + */ + List getIdListByAttrId(@Param("attrId") Long attrId); + + /** + * 批量更新 + * @param attrValues + */ + void updateBatch(@Param("attrValues") List attrValues); + + /** + * 批量删除 + * @param attrValueIds + */ + void deleteBatch(@Param("attrValueIds") List attrValueIds); + + /** + * 批量更新spu中的规格数据 + * @param spuAttrValues + */ + void updateBatchOfSpuAttrValue(@Param("spuAttrValues") List spuAttrValues); + + /** + * 批量更新sku中的规格数据 + * @param spuSkuAttrValues + */ + void updateBatchOfSpuSkuAttrValue(@Param("spuSkuAttrValues") List spuSkuAttrValues); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/BrandMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/BrandMapper.java new file mode 100644 index 00000000..e137eb8e --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/BrandMapper.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.dto.BrandDTO; +import com.mall4j.cloud.product.model.Brand; +import com.mall4j.cloud.api.product.vo.BrandVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface BrandMapper { + + /** + * 获取品牌信息列表 + * + * @param brandDTO 品牌信息 + * @return 品牌信息列表 + */ + List list(@Param("brandDTO") BrandDTO brandDTO); + + /** + * 根据品牌信息id获取品牌信息 + * + * @param brandId 品牌信息id + * @return 品牌信息 + */ + BrandVO getByBrandId(@Param("brandId") Long brandId); + + /** + * 保存品牌信息 + * + * @param brand 品牌信息 + */ + void save(@Param("brand") Brand brand); + + /** + * 更新品牌信息 + * + * @param brand 品牌信息 + */ + void update(@Param("brand") Brand brand); + + /** + * 根据品牌信息id删除品牌信息 + * + * @param brandId + */ + void deleteById(@Param("brandId") Long brandId); + + /** + * 获取品牌在商品中使用的数量 + * + * @param brandId + * @return 使用该品牌的商品数量 + */ + int getUseNum(@Param("brandId") Long brandId); + + /** + * 根据分类id,获取品牌数据 + * + * @param categoryId + * @return + */ + List getBrandByCategoryId(@Param("categoryId") Long categoryId); + + /** + * 更新品牌状态(启用或禁用) + * + * @param brandDTO + */ + void updateBrandStatus(@Param("brand") BrandDTO brandDTO); + + /** + * 根据分类id,获取品牌列表(分类中的推荐品牌) + * + * @param categoryId + * @return + */ + List listByCategory(@Param("categoryId") Long categoryId); + + /** + * 获取置顶品牌列表 + * @return + */ + List topBrandList(); + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryBrandMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryBrandMapper.java new file mode 100644 index 00000000..9313369a --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryBrandMapper.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.CategoryBrand; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 品牌分类关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface CategoryBrandMapper { + + /** + * 根据品牌分类关联信息id删除品牌分类关联信息 + * + * @param brandId + */ + void deleteByBrandId(@Param("brandId") Long brandId); + + /** + * 批量保存 + * @param categoryBrandList + */ + void saveBatch(@Param("categoryBrandList") List categoryBrandList); + + /** + * 根据品牌id获取关联的分类id + * @param brandId 品牌id + * @return 分类id列表 + */ + List getCategoryIdsByBrandId(@Param("brandId") Long brandId); + + /** + * 根据品牌id和分类id列表删除关联信息 + * @param brandId + * @param categoryIds + */ + void deleteByBrandIdAndCategoryIds(@Param("brandId") Long brandId, @Param("categoryIds") List categoryIds); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryMapper.java new file mode 100644 index 00000000..3b12de8c --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/CategoryMapper.java @@ -0,0 +1,124 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.Category; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Set; + +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface CategoryMapper { + + /** + * 根据分类信息id获取分类信息 + * + * @param categoryId 分类信息id + * @return 分类信息 + */ + CategoryVO getById(@Param("categoryId") Long categoryId); + + /** + * 保存分类信息 + * + * @param category 分类信息 + */ + void save(@Param("category") Category category); + + /** + * 更新分类信息 + * + * @param category 分类信息 + * @return 更新的数量 + */ + int update(@Param("category") Category category); + + /** + * 根据分类信息id删除分类信息 + * + * @param categoryId + */ + void deleteById(@Param("categoryId") Long categoryId); + + /** + * 获取分类被关联的数量 + * + * @param categoryId + * @return + */ + int getCategoryUseNum(@Param("categoryId") Long categoryId); + + /** + * 获取分类列表(未删除的分类--启用、未启用状态, 用于分类管理) + * + * @param shopId 店铺id 必填 + * @return + */ + List list(@Param("shopId") Long shopId); + + + /** + * 获取分类列表(仅启用状态的分类) + * + * @param parentId 不填默认 + * @param shopId 店铺id 必填 + * @return + */ + List listByShopIdAndParenId(@Param("shopId") Long shopId, @Param("parentId") Long parentId); + + /** + * 根据分类id 获取分类下的子分类 + * + * @param categoryId + * @return + */ + List getChildCategory(@Param("categoryId") Long categoryId); + + /** + * 批量更新分类状态(启用、禁用) + * + * @param categoryIds + * @param status + * @return + */ + int updateBatchOfStatus(@Param("categoryIds") List categoryIds, @Param("status") Integer status); + + /** + * 根据分类名获取分类的数量 + * + * @param categoryId + * @param name + * @return + */ + int getCountByName(@Param("categoryId") Long categoryId, @Param("name") String name); + + /** + * 根据分类id列表,获取分类列表 + * + * @param categoryIds + * @return + */ + List getListByCategoryIds(@Param("categoryIds") Set categoryIds); + + /** + * 获取分类id列表 + * @param shopId 店铺id + * @param parentId + * @return + */ + List listCategoryId(@Param("shopId") Long shopId, @Param("parentId") Long parentId); + + /** + * 获取分类及子分类的数据 + * + * @param shopId + * @param parentId + * @return + */ + List getCategoryAndChildCatogory(@Param("shopId") Long shopId, @Param("parentId") Long parentId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/ShopCartItemMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/ShopCartItemMapper.java new file mode 100644 index 00000000..7a70b48b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/ShopCartItemMapper.java @@ -0,0 +1,101 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.dto.shopcart.CheckShopCartItemDTO; +import com.mall4j.cloud.product.model.ShopCartItem; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 购物车 + * + * @author FrozenWatermelon + * @date 2020-11-21 10:01:23 + */ +public interface ShopCartItemMapper { + + /** + * 获取购物车列表 + * + * @return 购物车列表 + */ + List list(); + + /** + * 根据购物车id获取购物车 + * + * @param cartItemId 购物车id + * @return 购物车 + */ + ShopCartItemVO getByCartId(@Param("cartItemId") Long cartItemId); + + /** + * 保存购物车 + * + * @param shopCartItem 购物车 + */ + void save(@Param("shopCartItem") ShopCartItem shopCartItem); + + /** + * 更新购物车 + * + * @param shopCartItem 购物车 + */ + void update(@Param("shopCartItem") ShopCartItem shopCartItem); + + /** + * 根据购物车id删除购物车 + * + * @param cartItemId + */ + void deleteById(@Param("cartItemId") Long cartItemId); + + /** + * 根据用户id获取用户的购物车信息 + * + * @param userId 用户id + * @param isExpiry 是否已过期 + * @param isChecked 是否被选中 + * @return 购物车项 + */ + List getShopCartItems(@Param("userId") Long userId, @Param("isExpiry") Boolean isExpiry, @Param("isChecked") Boolean isChecked); + + /** + * 根据购物车项id删除购物车 + * + * @param userId 用户id + * @param shopCartItemIds 购物车项id + */ + void deleteShopCartItemsByShopCartItemIds(@Param("userId") Long userId, @Param("shopCartItemIds") List shopCartItemIds); + + /** + * 清空用户购物车 + * + * @param userId 用户id + */ + void deleteAllShopCartItems(@Param("userId") Long userId); + + /** + * 根据商品id,获取用户id,用于清空购物车商品数量的缓存 + * + * @param spuId 商品id + * @return 用户id列表 + */ + List listUserIdBySpuId(@Param("spuId") Long spuId); + + /** + * 购物项数量,有缓存 + * + * @param userId 用户id + * @return 购物项数量 + */ + Integer getShopCartItemCount(@Param("userId") Long userId); + + /** + * 勾选购车车状态 + * @param userId 用户id + * @param checkShopCartItems 参数 + */ + void checkShopCartItems(@Param("userId") Long userId, @Param("checkShopCartItems") List checkShopCartItems); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuMapper.java new file mode 100644 index 00000000..141e0ed9 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuMapper.java @@ -0,0 +1,91 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.model.Sku; +import com.mall4j.cloud.api.product.vo.SkuVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * sku信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SkuMapper { + + /** + * 保存sku信息 + * + * @param sku sku信息 + */ + void save(@Param("sku") Sku sku); + + /** + * 更新sku信息 + * + * @param sku sku信息 + */ + void update(@Param("sku") Sku sku); + + /** + * 根据sku信息id删除sku信息 + * + * @param skuId + */ + void deleteById(@Param("skuId") Long skuId); + + /** + * 根据spuId获取sku信息 + * + * @param spuId id + * @return 返回sku信息 + */ + List listBySpuId(@Param("spuId") Long spuId); + + /** + * 批量保存 + * + * @param skuList + */ + void saveBatch(@Param("skuList") List skuList); + + /** + * 根据spuId删除sku信息 + * + * @param spuId spuId + */ + void updateBySpuId(Long spuId); + + /** + * 批量修改 + * + * @param skus 修改后的信息 + */ + void updateBatch(@Param("skus") List skus); + + /** + * 获取商品详细信息 + * + * @param spuId + * @return + */ + List listBySpuIdAndExtendInfo(@Param("spuId") Long spuId); + + /** + * 根据skuid获取sku信息 + * + * @param skuId + * @return + */ + SkuVO getSkuBySkuId(@Param("skuId") Long skuId); + + /** + * 获取商品的sku列表(仅获取启用状态) + * + * @param spuId + * @return + */ + List getSkuBySpuId(@Param("spuId") Long spuId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockLockMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockLockMapper.java new file mode 100644 index 00000000..fc9a12ab --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockLockMapper.java @@ -0,0 +1,89 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.bo.SkuWithStockBO; +import com.mall4j.cloud.product.model.SkuStockLock; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +public interface SkuStockLockMapper { + + /** + * 获取库存锁定信息列表 + * + * @return 库存锁定信息列表 + */ + List list(); + + /** + * 根据库存锁定信息id获取库存锁定信息 + * + * @param id 库存锁定信息id + * @return 库存锁定信息 + */ + SkuStockLock getById(@Param("id") Long id); + + /** + * 保存库存锁定信息 + * + * @param skuStockLock 库存锁定信息 + */ + void save(@Param("skuStockLock") SkuStockLock skuStockLock); + + /** + * 更新库存锁定信息 + * + * @param skuStockLock 库存锁定信息 + */ + void update(@Param("skuStockLock") SkuStockLock skuStockLock); + + /** + * 根据库存锁定信息id删除库存锁定信息 + * + * @param id + */ + void deleteById(@Param("id") Long id); + + /** + * 批量保存库存锁定信息 + * + * @param skuStockLocks 库存锁定信息 + */ + void saveBatch(@Param("skuStockLocks") List skuStockLocks); + + /** + * 根据订单号获取锁定的库存 + * + * @param orderIds 订单号 + * @return 锁定的库存信息 + */ + List listByOrderIds(@Param("orderIds") List orderIds); + + /** + * 将锁定状态标记为已解锁 + * + * @param lockIds ids + */ + void unLockByIds(@Param("lockIds") List lockIds); + + /** + * 正式锁定库存,标记为使用状态 + * + * @param orderIds 锁定库存的订单 + */ + void markerStockUse(@Param("orderIds") List orderIds); + + /** + * 根据订单号获取已取消锁定的库存 + * + * @param orderIds 订单号 + * @return 锁定的库存信息 + */ + List listUnLockByOrderIds(@Param("orderIds") List orderIds); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockMapper.java new file mode 100644 index 00000000..62b5f5f9 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SkuStockMapper.java @@ -0,0 +1,99 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.bo.SkuWithStockBO; +import com.mall4j.cloud.product.model.SkuStock; +import com.mall4j.cloud.product.vo.SkuStockVO; +import com.mall4j.cloud.api.product.vo.SkuVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 库存信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SkuStockMapper { + + /** + * 保存库存信息 + * + * @param skuStock 库存信息 + */ + void save(@Param("skuStock") SkuStock skuStock); + + /** + * 更新库存信息 + * + * @param skuStock 库存信息 + */ + void update(@Param("skuStock") SkuStock skuStock); + + /** + * 根据库存信息id删除库存信息 + * + * @param stockId + */ + void deleteById(@Param("stockId") Long stockId); + + /** + * 批量保存 + * + * @param skuStocks 库存信息集合 + */ + void saveBatch(@Param("skuStocks") List skuStocks); + + /** + * 根据skuIds删除库存 + * + * @param skuIds ids + */ + void deleteBySkuIds(@Param("skuIds") List skuIds); + + /** + * 根据列表中的库存数量,增加sku的库存 + * + * @param skuStocks 修改信息 + */ + void updateStock(@Param("skuStocks") List skuStocks); + + /** + * 通过sku集合获取库存信息 + * + * @param skuVOList + * @return 库存信息 + */ + List listBySkuList(@Param("skuVOList") List skuVOList); + + /** + * 通过订单减少库存 + * + * @param skuId 商品id + * @param count 数量 + * @return + */ + int reduceStockByOrder(@Param("skuId") Long skuId, @Param("count") Integer count); + + /** + * 通过订单添加库存 + * + * @param skuWithStocks 库存信息 + */ + void addStockByOrder(@Param("skuWithStocks") List skuWithStocks); + + /** + * 通过订单减少实际库存 + * + * @param skuWithStocks 库存信息 + */ + void reduceActualStockByOrder(@Param("skuWithStocks") List skuWithStocks); + + /** + * 通过已经取消的订单减少实际库存 + * + * @param skuWithStocks 库存信息 + */ + void reduceActualStockByCancelOrder(@Param("skuWithStocks") List skuWithStocks); + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuAttrValueMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuAttrValueMapper.java new file mode 100644 index 00000000..d09bc723 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuAttrValueMapper.java @@ -0,0 +1,89 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.api.product.vo.SpuAttrValueVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 商品规格属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuAttrValueMapper { + + /** + * 保存商品规格属性关联信息 + * + * @param spuAttrValue 商品规格属性关联信息 + */ + void save(@Param("spuAttrValue") SpuAttrValue spuAttrValue); + + /** + * 更新商品规格属性关联信息 + * + * @param spuAttrValue 商品规格属性关联信息 + */ + void update(@Param("spuAttrValue") SpuAttrValue spuAttrValue); + + /** + * 批量保存 + * + * @param spuAttrValues 规格list + */ + void saveBatch(@Param("spuAttrValues") List spuAttrValues); + + /** + * 根据spuId删除对应规格属性 + * + * @param spuId id + */ + void deleteBySpuId(Long spuId); + + /** + * 根据属性和分类id列表删除商品属性关联信息 + * + * @param attrId 属性id + * @param attrValueIds 属性值id + * @param categoryIds 分类id列表 + */ + void deleteByAttIdAndCategoryIds(@Param("attrId") Long attrId, @Param("attrValueIds") List attrValueIds, @Param("categoryIds") List categoryIds); + + /** + * 根据spuId获取商品属性列表 + * + * @param spuId + * @return + */ + List getSpuAttrsBySpuId(@Param("spuId") Long spuId); + + /** + * 批量更新商品属性列表 + * + * @param spuAttrValues + */ + void updateBatch(@Param("spuAttrValues") List spuAttrValues); + + /** + * 批量删除商品属性 + * + * @param spuAttrValueIds + */ + void deleteBatch(@Param("spuAttrValueIds") List spuAttrValueIds); + + /** + * 根据属性值id,获取spuId列表 + * + * @param attrValueIds + * @return + */ + List getShopIdByAttrValueIds(@Param("attrValueIds") List attrValueIds); + + /** + * 批量更新商品基本属性 + * @param spuAttrValues + */ + void batchUpdateSpuAttrValue(@Param("spuAttrValues") List spuAttrValues); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuDetailMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuDetailMapper.java new file mode 100644 index 00000000..48e12f53 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuDetailMapper.java @@ -0,0 +1,32 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.SpuDetail; +import org.apache.ibatis.annotations.Param; + +/** + * 商品详情信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuDetailMapper { + + /** + * 保存商品详情信息 + * @param spuDetail 商品详情信息 + */ + void save(@Param("spuDetail") SpuDetail spuDetail); + + /** + * 更新商品详情信息 + * @param spuDetail 商品详情信息 + */ + void update(@Param("spuDetail") SpuDetail spuDetail); + + /** + * 根据商品详情信息id删除商品详情信息 + * @param spuId + */ + void deleteById(@Param("spuId") Long spuId); + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuExtensionMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuExtensionMapper.java new file mode 100644 index 00000000..218446c1 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuExtensionMapper.java @@ -0,0 +1,90 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.bo.SkuWithStockBO; +import com.mall4j.cloud.product.model.SpuExtension; +import com.mall4j.cloud.product.vo.SpuExtensionVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +/** + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public interface SpuExtensionMapper { + + /** + * 获取列表 + * + * @return 列表 + */ + List list(); + + /** + * 根据id获取 + * + * @param spuExtendId id + * @return + */ + SpuExtensionVO getBySpuExtendId(@Param("spuExtendId") Long spuExtendId); + + /** + * 保存 + * + * @param spuExtension + */ + void save(@Param("spuExtension") SpuExtension spuExtension); + + /** + * 根据id删除 + * + * @param spuId id + */ + void deleteById(@Param("spuId") Long spuId); + + /** + * 更新库存 + * + * @param spuId 商品id + * @param count 商品数量 + */ + void updateStock(@Param("spuId") Long spuId, @Param("count") Integer count); + + /** + * 通过订单减少库存 + * + * @param spuId 商品id + * @param count 数量 + * @return + */ + int reduceStockByOrder(@Param("spuId") Long spuId, @Param("count") Integer count); + + /** + * 通过订单添加库存 + * + * @param skuWithStocks 库存信息 + */ + void addStockByOrder(@Param("skuWithStocks") List skuWithStocks); + + /** + * 通过订单减少实际库存 + * + * @param skuWithStocks 库存信息 + */ + void reduceActualStockByOrder(@Param("skuWithStocks") List skuWithStocks); + + /** + * 通过已经取消的订单减少实际库存 + * + * @param skuWithStocks 库存信息 + */ + void reduceActualStockByCancelOrder(@Param("skuWithStocks") List skuWithStocks); + + /** + * 获取spu扩展信息 + * @param spuId + * @return + */ + SpuExtension getBySpuId(Long spuId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuMapper.java new file mode 100644 index 00000000..3265e1cc --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuMapper.java @@ -0,0 +1,134 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.dto.SpuPageSearchDTO; +import com.mall4j.cloud.product.model.Spu; +import com.mall4j.cloud.api.product.vo.SpuVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuMapper { + + /** + * 获取spu信息列表 + * + * @param spu + * @return spu信息列表 + */ + List list(@Param("spu") SpuPageSearchDTO spu); + + /** + * 根据spu信息id获取spu信息 + * + * @param spuId spu信息id + * @return spu信息 + */ + SpuVO getBySpuId(@Param("spuId") Long spuId); + + /** + * 保存spu信息 + * + * @param spu spu信息 + */ + void save(@Param("spu") Spu spu); + + /** + * 更新spu信息 + * + * @param spu spu信息 + */ + void update(@Param("spu") Spu spu); + + /** + * 根据spu信息id删除spu信息 + * + * @param spuId + */ + void deleteById(@Param("spuId") Long spuId); + + /** + * 根据商品id修改商品状态 + * + * @param spuId 商品id + */ + void updateStatusBySpuId(@Param("spuId") Long spuId); + + /** + * 根据spuId获取商品信息(搜索) + * + * @param spuId + * @return 商品信息 + */ + EsProductBO loadEsProductBO(@Param("spuId") Long spuId); + + /** + * 获取 spuId列表 + * + * @param shopCategoryIds 店铺分类id列表 + * @param categoryIds 平台分类Id列表 + * @param brandId 品牌id + * @param shopId 店铺id + * @return spuId列表 + */ + List getSpuIdsBySpuUpdateDTO(@Param("shopCategoryIds") List shopCategoryIds, @Param("categoryIds") List categoryIds + , @Param("brandId") Long brandId, @Param("shopId") Long shopId); + + /** + * 改变商品状态(上下架) + * + * @param spuId + * @param status + */ + void changeSpuStatus(@Param("spuId") Long spuId, @Param("status") Integer status); + + /** + * 更新spu表(canal监听后,会发送更新的消息,更新es中的数据) + * + * @param spuIds + * @param categoryIds + */ + void updateSpuUpdateTime(@Param("spuIds") List spuIds, @Param("categoryIds") List categoryIds); + + /** + * 根据店铺id获取spu列表 + * @param spuIds 商品ids + * @param spuName 商品名称 + * @param isFailure 是否失效 + * @return 商品列表信息 + */ + List listBySpuIds(@Param("spuIds") List spuIds, @Param("spuName") String spuName, @Param("isFailure") Integer isFailure); + + /** + * 根据分组id和是否为当前分组,返回商品列表 + * @param pageDTO 分页信息 + * @param spuDTO 分组信息 + * @param isContain 是否当前分组商品 1.参与 0.不参与 + * @return 商品列表 + */ + List pageByLangAndTagId(@Param("pageDTO") PageDTO pageDTO, @Param("spuDTO") SpuDTO spuDTO, @Param("isContain") Integer isContain); + + /** + * 根据分类id列表批量获取商品id列表 + * @param cidList + * @param type + * @param shopId + * @return + */ + List listIdsByCidListAndTypeAndShopId(@Param("cidList") List cidList, @Param("type") int type, @Param("shopId") Long shopId); + + /** + * 根据商品id列表与状态批量修改商品状态 + * @param spuIdList + * @param status + */ + void batchChangeSpuStatusBySpuIdsAndStatus(@Param("spuIdList") List spuIdList, @Param("status") Integer status); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuSkuAttrValueMapper.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuSkuAttrValueMapper.java new file mode 100644 index 00000000..45040080 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/mapper/SpuSkuAttrValueMapper.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.product.mapper; + +import com.mall4j.cloud.product.model.SpuSkuAttrValue; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 商品sku销售属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuSkuAttrValueMapper { + + /** + * 保存商品sku销售属性关联信息 + * + * @param spuSkuAttrValue 商品sku销售属性关联信息 + */ + void save(@Param("spuSkuAttrValue") SpuSkuAttrValue spuSkuAttrValue); + + /** + * 更新商品sku销售属性关联信息 + * + * @param spuSkuAttrValue 商品sku销售属性关联信息 + */ + void update(@Param("spuSkuAttrValue") SpuSkuAttrValue spuSkuAttrValue); + + /** + * 批量更新商品sku销售属性关联信息 + * + * @param spuSkuAttrValues 商品sku销售属性关联信息 + */ + void updateBatch(@Param("spuSkuAttrValues") List spuSkuAttrValues); + + /** + * 根据商品sku销售属性关联信息id删除商品sku销售属性关联信息 + * + * @param spuSkuAttrId + */ + void deleteById(@Param("spuSkuAttrId") Long spuSkuAttrId); + + /** + * 批量保存sku规格信息 + * + * @param spuSkuAttrValues attrList + */ + void saveBatch(@Param("spuSkuAttrValues") List spuSkuAttrValues); + + /** + * 修改商品规格信息 + * + * @param spuId + */ + void updateBySpuId(@Param("spuId") Long spuId); + + /** + * 根据skuId列表,改变销售属性状态 + * @param skuIds + * @param status + */ + void changeStatusBySkuId(@Param("skuIds") List skuIds, @Param("status") Integer status); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Attr.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Attr.java new file mode 100644 index 00000000..7f3eb8d6 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Attr.java @@ -0,0 +1,123 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; +import java.util.List; + +import com.mall4j.cloud.common.model.BaseModel; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +public class Attr extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + + /** + * attr id + */ + private Long attrId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 属性名称 + */ + private String name; + + /** + * 属性描述 + */ + private String desc; + + /** + * 作为搜索参数 0:不需要,1:需要 + */ + private Integer searchType; + + /** + * 属性类型 0:销售属性,1:基本属性 + */ + private Integer attrType; + + /** + * 属性值列表 + */ + private List attrValues; + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Integer getSearchType() { + return searchType; + } + + public void setSearchType(Integer searchType) { + this.searchType = searchType; + } + + public Integer getAttrType() { + return attrType; + } + + public void setAttrType(Integer attrType) { + this.attrType = attrType; + } + + public List getAttrValues() { + return attrValues; + } + + public void setAttrValues(List attrValues) { + this.attrValues = attrValues; + } + + @Override + public String toString() { + return "Attr{" + + "attrId=" + attrId + + ", shopId='" + shopId + '\'' + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", searchType=" + searchType + + ", attrType=" + attrType + + ", attrValues=" + attrValues + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrCategory.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrCategory.java new file mode 100644 index 00000000..83c54a69 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrCategory.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 属性与属性分组关联信息 + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +public class AttrCategory extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 属性与分类关联id + */ + private Long attrCategoryId; + + /** + * 分类id + */ + private Long categoryId; + + /** + * 属性id + */ + private Long attrId; + + public Long getAttrCategoryId() { + return attrCategoryId; + } + + public void setAttrCategoryId(Long attrCategoryId) { + this.attrCategoryId = attrCategoryId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + @Override + public String toString() { + return "AttrCategory{" + + "attrCategoryId=" + attrCategoryId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",categoryId=" + categoryId + + ",attrId=" + attrId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrValue.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrValue.java new file mode 100644 index 00000000..2740a862 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/AttrValue.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 属性值信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class AttrValue extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 属性id + */ + private Long attrValueId; + + /** + * 属性ID + */ + private Long attrId; + + /** + * 属性值 + */ + private String value; + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "AttrValue{" + + "attrValueId=" + attrValueId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",attrId=" + attrId + + ",value=" + value + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Brand.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Brand.java new file mode 100644 index 00000000..65c97ff3 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Brand.java @@ -0,0 +1,121 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class Brand extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * brand_id + */ + private Long brandId; + + /** + * 品牌名称 + */ + private String name; + + /** + * 品牌描述 + */ + private String desc; + + /** + * 品牌logo图片 + */ + private String imgUrl; + + /** + * 检索首字母 + */ + private String firstLetter; + + /** + * 排序 + */ + private Integer seq; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public String getFirstLetter() { + return firstLetter; + } + + public void setFirstLetter(String firstLetter) { + this.firstLetter = firstLetter; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "Brand{" + + "brandId=" + brandId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",name=" + name + + ",desc=" + desc + + ",imgUrl=" + imgUrl + + ",firstLetter=" + firstLetter + + ",seq=" + seq + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Category.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Category.java new file mode 100644 index 00000000..494892a8 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Category.java @@ -0,0 +1,176 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class Category extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 分类id + */ + private Long categoryId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 父ID + */ + private Long parentId; + + /** + * 分类名称 + */ + private String name; + + /** + * 分类描述 + */ + private String desc; + + /** + * 分类地址{parent_id}-{child_id},... + */ + private String path; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + /** + * 分类图标 + */ + private String icon; + + /** + * 分类的显示图片 + */ + private String imgUrl; + + /** + * 分类层级 从0开始 + */ + private Integer level; + + /** + * 排序 + */ + private Integer seq; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "Category{" + + "categoryId=" + categoryId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",shopId=" + shopId + + ",parentId=" + parentId + + ",name=" + name + + ",desc=" + desc + + ",path=" + path + + ",status=" + status + + ",icon=" + icon + + ",imgUrl=" + imgUrl + + ",level=" + level + + ",seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/CategoryBrand.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/CategoryBrand.java new file mode 100644 index 00000000..62522903 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/CategoryBrand.java @@ -0,0 +1,64 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 品牌分类关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class CategoryBrand extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * + */ + private Long id; + + /** + * 品牌id + */ + private Long brandId; + + /** + * 分类id + */ + private Long categoryId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + @Override + public String toString() { + return "CategoryBrand{" + + "id=" + id + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",brandId=" + brandId + + ",categoryId=" + categoryId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/ShopCartItem.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/ShopCartItem.java new file mode 100644 index 00000000..5e5bf709 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/ShopCartItem.java @@ -0,0 +1,133 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 购物车 + * + * @author FrozenWatermelon + * @date 2020-11-21 10:01:23 + */ +public class ShopCartItem extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long cartItemId; + + /** + * 店铺ID + */ + private Long shopId; + + /** + * 产品ID + */ + private Long spuId; + + /** + * SkuID + */ + private Long skuId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 购物车产品个数 + */ + private Integer count; + + /** + * 售价,加入购物车时的商品价格 + */ + private Long priceFee; + + /** + * 是否已经勾选 + */ + private Integer isChecked; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + + public Integer getIsChecked() { + return isChecked; + } + + public void setIsChecked(Integer isChecked) { + this.isChecked = isChecked; + } + + public Long getCartItemId() { + return cartItemId; + } + + public void setCartItemId(Long cartItemId) { + this.cartItemId = cartItemId; + } + + @Override + public String toString() { + return "ShopCartItem{" + + "cartItemId=" + cartItemId + + ", shopId=" + shopId + + ", spuId=" + spuId + + ", skuId=" + skuId + + ", userId=" + userId + + ", count=" + count + + ", priceFee=" + priceFee + + ", isChecked=" + isChecked + + "} " + super.toString(); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Sku.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Sku.java new file mode 100644 index 00000000..a8db36ae --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Sku.java @@ -0,0 +1,189 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; +import java.math.BigDecimal; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * sku信息 + * + * @author FrozenWatermelon + * @date 2020-12-08 15:54:22 + */ +public class Sku extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 属性id + */ + private Long skuId; + + /** + * SPU id + */ + private Long spuId; + + /** + * 多个销售属性值id逗号分隔 + */ + private String attrs; + + /** + * sku名称 + */ + private String skuName; + + /** + * sku图片 + */ + private String imgUrl; + + /** + * 售价,整数方式保存 + */ + private Long priceFee; + + /** + * 市场价,整数方式保存 + */ + private Long marketPriceFee; + + /** + * 商品编码 + */ + private String partyCode; + + /** + * 商品条形码 + */ + private String modelId; + + /** + * 商品重量 + */ + private BigDecimal weight; + + /** + * 商品体积 + */ + private BigDecimal volume; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getAttrs() { + return attrs; + } + + public void setAttrs(String attrs) { + this.attrs = attrs; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public String getPartyCode() { + return partyCode; + } + + public void setPartyCode(String partyCode) { + this.partyCode = partyCode; + } + + public String getModelId() { + return modelId; + } + + public void setModelId(String modelId) { + this.modelId = modelId; + } + + public BigDecimal getWeight() { + return weight; + } + + public void setWeight(BigDecimal weight) { + this.weight = weight; + } + + public BigDecimal getVolume() { + return volume; + } + + public void setVolume(BigDecimal volume) { + this.volume = volume; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "Sku{" + + "skuId=" + skuId + + ", spuId=" + spuId + + ", attrs='" + attrs + '\'' + + ", skuName='" + skuName + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", partyCode='" + partyCode + '\'' + + ", modelId='" + modelId + '\'' + + ", weight=" + weight + + ", volume=" + volume + + ", status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStock.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStock.java new file mode 100644 index 00000000..46e14413 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStock.java @@ -0,0 +1,93 @@ +package com.mall4j.cloud.product.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 库存信息 + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public class SkuStock extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 库存id + */ + private Long stockId; + + /** + * SKU ID + */ + private Long skuId; + + /** + * 实际库存 + */ + private Integer actualStock; + + /** + * 锁定库存 + */ + private Integer lockStock; + + /** + * 可售卖库存 + */ + private Integer stock; + + public Long getStockId() { + return stockId; + } + + public void setStockId(Long stockId) { + this.stockId = stockId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Integer getActualStock() { + return actualStock; + } + + public void setActualStock(Integer actualStock) { + this.actualStock = actualStock; + } + + public Integer getLockStock() { + return lockStock; + } + + public void setLockStock(Integer lockStock) { + this.lockStock = lockStock; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "SkuStock{" + + "stockId=" + stockId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",skuId=" + skuId + + ",actualStock=" + actualStock + + ",lockStock=" + lockStock + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStockLock.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStockLock.java new file mode 100644 index 00000000..788a6f84 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SkuStockLock.java @@ -0,0 +1,106 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +public class SkuStockLock extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * id + */ + private Long id; + + /** + * 商品id + */ + private Long spuId; + + /** + * sku id + */ + private Long skuId; + + /** + * 订单id + */ + private Long orderId; + + /** + * 锁定库存数量 + */ + private Integer count; + + /** + * 状态-1已解锁 0待确定 1已锁定 + */ + private Integer status; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SkuStockLock{" + + "id=" + id + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",skuId=" + skuId + + ",orderId=" + orderId + + ",count=" + count + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Spu.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Spu.java new file mode 100644 index 00000000..836e9e88 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/Spu.java @@ -0,0 +1,227 @@ +package com.mall4j.cloud.product.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-12-08 15:54:23 + */ +public class Spu extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * spu id + */ + private Long spuId; + + /** + * 品牌ID + */ + private Long brandId; + + /** + * 分类ID + */ + private Long categoryId; + + /** + * 店铺分类ID + */ + private Long shopCategoryId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * spu名称 + */ + private String name; + + /** + * 卖点 + */ + private String sellingPoint; + + /** + * 主图 + */ + private String mainImgUrl; + + /** + * 商品图片 多个图片逗号分隔 + */ + private String imgUrls; + + /** + * 商品视频 + */ + private String video; + + /** + * 售价,整数方式保存 + */ + private Long priceFee; + + /** + * 市场价,整数方式保存 + */ + private Long marketPriceFee; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + private Integer hasSkuImg; + + /** + * 序号 + */ + private Integer seq; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopCategoryId() { + return shopCategoryId; + } + + public void setShopCategoryId(Long shopCategoryId) { + this.shopCategoryId = shopCategoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public String getImgUrls() { + return imgUrls; + } + + public void setImgUrls(String imgUrls) { + this.imgUrls = imgUrls; + } + + public String getVideo() { + return video; + } + + public void setVideo(String video) { + this.video = video; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public Integer getHasSkuImg() { + return hasSkuImg; + } + + public void setHasSkuImg(Integer hasSkuImg) { + this.hasSkuImg = hasSkuImg; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "Spu{" + + "spuId=" + spuId + + ", brandId=" + brandId + + ", categoryId=" + categoryId + + ", shopCategoryId=" + shopCategoryId + + ", name='" + name + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", imgUrls='" + imgUrls + '\'' + + ", video='" + video + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", status=" + status + + ", hasSkuImg=" + hasSkuImg + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuAttrValue.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuAttrValue.java new file mode 100644 index 00000000..64728173 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuAttrValue.java @@ -0,0 +1,118 @@ +package com.mall4j.cloud.product.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 商品规格属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuAttrValue extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 商品属性值关联信息id + */ + private Long spuAttrValueId; + + /** + * 商品id + */ + private Long spuId; + + /** + * 规格属性id + */ + private Long attrId; + + /** + * 规格属性名称 + */ + private String attrName; + + /** + * 规格属性值id + */ + private Long attrValueId; + + /** + * 规格属性值名称 + */ + private String attrValueName; + + /** + * 规格属性描述 + */ + private String attrDesc; + + public Long getSpuAttrValueId() { + return spuAttrValueId; + } + + public void setSpuAttrValueId(Long spuAttrValueId) { + this.spuAttrValueId = spuAttrValueId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + public String getAttrDesc() { + return attrDesc; + } + + public void setAttrDesc(String attrDesc) { + this.attrDesc = attrDesc; + } + + @Override + public String toString() { + return "SpuAttrValue{" + + "spuAttrValueId=" + spuAttrValueId + + ",spuId=" + spuId + + ",attrId=" + attrId + + ",attrName=" + attrName + + ",attrValueId=" + attrValueId + + ",attrValueName=" + attrValueName + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuDetail.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuDetail.java new file mode 100644 index 00000000..9f85ce0f --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuDetail.java @@ -0,0 +1,51 @@ +package com.mall4j.cloud.product.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 商品详情信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuDetail extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 商品id + */ + private Long spuId; + + /** + * 商品详情 + */ + private String detail; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + @Override + public String toString() { + return "SpuDetail{" + + "spuId=" + spuId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",detail=" + detail + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuExtension.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuExtension.java new file mode 100644 index 00000000..2423d1b7 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuExtension.java @@ -0,0 +1,118 @@ +package com.mall4j.cloud.product.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public class SpuExtension extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 商品扩展信息表id + */ + private Long spuExtendId; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 商品id + */ + private Long spuId; + + /** + * 销量 + */ + private Integer saleNum; + + /** + * 实际库存 + */ + private Integer actualStock; + + /** + * 锁定库存 + */ + private Integer lockStock; + + /** + * 可售卖库存 + */ + private Integer stock; + + public Long getSpuExtendId() { + return spuExtendId; + } + + public void setSpuExtendId(Long spuExtendId) { + this.spuExtendId = spuExtendId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public Integer getActualStock() { + return actualStock; + } + + public void setActualStock(Integer actualStock) { + this.actualStock = actualStock; + } + + public Integer getLockStock() { + return lockStock; + } + + public void setLockStock(Integer lockStock) { + this.lockStock = lockStock; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "SpuExtension{" + + "spuExtendId=" + spuExtendId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",saleNum=" + saleNum + + ",actualStock=" + actualStock + + ",lockStock=" + lockStock + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuSkuAttrValue.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuSkuAttrValue.java new file mode 100644 index 00000000..1f209814 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/model/SpuSkuAttrValue.java @@ -0,0 +1,135 @@ +package com.mall4j.cloud.product.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 商品sku销售属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuSkuAttrValue extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 商品sku销售属性关联信息id + */ + private Long spuSkuAttrId; + + /** + * SPU ID + */ + private Long spuId; + + /** + * SKU ID + */ + private Long skuId; + + /** + * 销售属性ID + */ + private Long attrId; + + /** + * 销售属性名称 + */ + private String attrName; + + /** + * 销售属性值ID + */ + private Long attrValueId; + + /** + * 销售属性值 + */ + private String attrValueName; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + public Long getSpuSkuAttrId() { + return spuSkuAttrId; + } + + public void setSpuSkuAttrId(Long spuSkuAttrId) { + this.spuSkuAttrId = spuSkuAttrId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getAttrId() { + return attrId; + } + + public void setAttrId(Long attrId) { + this.attrId = attrId; + } + + public String getAttrName() { + return attrName; + } + + public void setAttrName(String attrName) { + this.attrName = attrName; + } + + public Long getAttrValueId() { + return attrValueId; + } + + public void setAttrValueId(Long attrValueId) { + this.attrValueId = attrValueId; + } + + public String getAttrValueName() { + return attrValueName; + } + + public void setAttrValueName(String attrValueName) { + this.attrValueName = attrValueName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SpuSkuAttrValue{" + + "spuSkuAttrId=" + spuSkuAttrId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",skuId=" + skuId + + ",attrId=" + attrId + + ",attrName=" + attrName + + ",attrValueId=" + attrValueId + + ",attrValueName=" + attrValueName + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrCategoryService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrCategoryService.java new file mode 100644 index 00000000..c0e735b4 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrCategoryService.java @@ -0,0 +1,36 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.api.product.vo.CategoryVO; + +import java.util.List; + +/** + * 属性与属性分组关联信息 + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +public interface AttrCategoryService { + + /** + * 保存属性与属性分组关联信息 + * @param attrId 属性id + * @param categoryId 分类id列表 + */ + void save(Long attrId, List categoryId); + + /** + * 更新属性与属性分组关联信息 + * @param attrId + * @param categoryId + * @return + */ + List update(Long attrId, List categoryId); + + /** + * 根据属性Id,获取属性关联的分类列表信息 + * @param attrId + * @return + */ + List listByAttrId(Long attrId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrService.java new file mode 100644 index 00000000..a8f1dd64 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrService.java @@ -0,0 +1,81 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.product.dto.AttrDTO; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.api.product.vo.AttrVO; + +import java.util.List; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +public interface AttrService { + + /** + * 分页获取属性信息列表 + * @param pageDTO 分页参数 + * @param attrDTO + * @return 属性信息列表分页数据 + */ + PageVO page(PageDTO pageDTO, AttrDTO attrDTO); + + /** + * 根据属性信息id获取属性信息 + * + * @param attrId 属性信息id + * @return 属性信息 + */ + AttrVO getByAttrId(Long attrId); + + /** + * 保存属性信息 + * @param attr 属性信息 + * @param categoryIds 分类id列表 + */ + void save(Attr attr, List categoryIds); + + /** + * 更新属性信息 + * @param attr 属性信息 + * @param categoryIds 分类id列表 + */ + void update(Attr attr, List categoryIds); + + /** + * 根据属性信息id删除属性信息 + * @param attrId + */ + void deleteById(Long attrId); + + /** + * 根据分类和属性类型,获取对应的属性列表 + * @param categoryId + * @return + */ + List getAttrsByCategoryIdAndAttrType(Long categoryId); + + /** + * 根据属性id获取属性下的分类id列表 + * @param attrId + * @return + */ + List getAttrOfCategoryIdByAttrId(Long attrId); + + /** + * 清除属性关联的分类列表中属性列表的缓存 + * @param categoryIds 属性关联的分类列表 + */ + void removeAttrByCategoryId(List categoryIds); + + /** + * 获取店铺中的销售属性 + * @param shopId + * @return + */ + List getShopAttrs(Long shopId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrValueService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrValueService.java new file mode 100644 index 00000000..6654436e --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/AttrValueService.java @@ -0,0 +1,29 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.api.product.vo.AttrVO; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.product.model.AttrValue; + +import java.util.List; + +/** + * 属性值信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface AttrValueService { + /** + * 根据属性值信息和属性id,保存属性值信息 + * @param attrValues + * @param attrId + */ + void saveByAttrValuesAndAttrId(List attrValues, Long attrId); + + /** + * 根据属性值信息和属性id,更新属性值信息 + * @param attrVO + * @param dbAttr + */ + void update(Attr attrVO, AttrVO dbAttr); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/BrandService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/BrandService.java new file mode 100644 index 00000000..a43f5e3e --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/BrandService.java @@ -0,0 +1,87 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.product.dto.BrandDTO; +import com.mall4j.cloud.product.model.Brand; +import com.mall4j.cloud.api.product.vo.BrandVO; + +import java.util.List; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface BrandService { + + /** + * 分页获取品牌信息列表 + * @param pageDTO 分页参数 + * @param brandDTO + * @return 品牌信息列表分页数据 + */ + PageVO page(PageDTO pageDTO, BrandDTO brandDTO); + + /** + * 根据品牌信息id获取品牌信息 + * + * @param brandId 品牌信息id + * @return 品牌信息 + */ + BrandVO getByBrandId(Long brandId); + + /** + * 保存品牌信息 + * @param brand 品牌信息 + * @param categoryIds 分类id列表 + */ + void save(Brand brand, List categoryIds); + + /** + * 更新品牌信息 + * @param brand 品牌信息 + * @param categoryIds 分类id列表 + */ + void update(Brand brand, List categoryIds); + + /** + * 根据品牌id,删除品牌 + * @param brandId + */ + void deleteById(Long brandId); + + /** + * 根据分类id,获取品牌数据 + * @param categoryId + * @return + */ + List getBrandByCategoryId(Long categoryId); + + /** + * 更新品牌状态(启用或禁用) + * @param brandDTO + * @return + */ + void updateBrandStatus(BrandDTO brandDTO); + + /** + * 根据分类id,获取品牌列表 + * @param categoryId + * @return + */ + List listByCategory(Long categoryId); + + /** + * 获取置顶品牌列表 + * @return + */ + List topBrandList(); + + /** + * 清楚分类缓存 + * @param categoryIds + */ + void removeCache(List categoryIds); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryBrandService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryBrandService.java new file mode 100644 index 00000000..2f1ff4cd --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryBrandService.java @@ -0,0 +1,39 @@ +package com.mall4j.cloud.product.service; + +import java.util.List; + +/** + * 品牌分类关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface CategoryBrandService { + + /** + * 根据品牌id删除品牌分类关联信息 + * @param brandId + */ + void deleteByBrandId(Long brandId); + + /** + * 保存品牌信息 + * @param brandId + * @param categoryIds + */ + void saveByCategoryIds(Long brandId, List categoryIds); + + /** + * 更新品牌信息 + * @param brandId + * @param categoryIds + */ + void updateByCategoryIds(Long brandId, List categoryIds); + + /** + * 根据品牌id或者关联的分类列表 + * @param brandId + * @return + */ + List getCategoryIdBrandId(Long brandId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryService.java new file mode 100644 index 00000000..23c592ec --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/CategoryService.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.dto.CategoryDTO; +import com.mall4j.cloud.product.model.Category; +import com.mall4j.cloud.api.product.vo.CategoryVO; + +import java.util.List; + +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface CategoryService { + + /** + * 根据分类信息id获取分类信息 + * + * @param categoryId 分类信息id + * @return 分类信息 + */ + CategoryVO getById(Long categoryId); + + /** + * 保存分类信息 + * @param category 分类信息 + */ + void save(Category category); + + /** + * 更新分类信息 + * @param category 分类信息 + */ + void update(Category category); + + /** + * 根据分类信息id删除分类信息 + * @param categoryId + */ + void deleteById(Long categoryId); + + /** + * 获取分类列表(未删除的分类--启用、未启用状态的分类) + * @param shopId 店铺id 必填 + * @return + */ + List list(Long shopId); + + /** + * 获取分类id列表 + * @param shopId + * @param parentId + * @return + */ + List listCategoryId(Long shopId, Long parentId); + + /** + * 根据shopId 和 categoruId 清除分类缓存 + * @param shopId + * @param parentId + */ + void removeCategoryCache(Long shopId, Long parentId); + + /** + * 分类的启用和禁用 + * @param categoryDTO + * @return + */ + Boolean categoryEnableOrDisable(CategoryDTO categoryDTO); + + /** + * 获取分类的pathName集合 + * @param categorys 分类集合 + */ + void getPathNames(List categorys); + + /** + * 根据分类id获取分类、pathName信息 + * @param categoryId + * @return + */ + CategoryVO getPathNameByCategoryId(Long categoryId); + + /** + * 获取分类列表 (仅返回启用状态的分类) + * @param parentId 上级分类id + * @param shopId 店铺id + * @return + */ + List categoryList(Long shopId, Long parentId); + + /** + * 根据店铺id和上级id,获取分类列表 + * @param parentId 上级分类id + * @param shopId 店铺id + * @return + */ + List listByShopIdAndParenId(Long shopId, Long parentId); + + /** + * 获取分类列表 (仅获取启用状态的分类) + * @param shopId + * @return + */ + List shopCategoryList(Long shopId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/ShopCartService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/ShopCartService.java new file mode 100644 index 00000000..61e5fcb2 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/ShopCartService.java @@ -0,0 +1,84 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.dto.shopcart.ChangeShopCartItemDTO; +import com.mall4j.cloud.product.dto.shopcart.CheckShopCartItemDTO; +import com.mall4j.cloud.product.model.ShopCartItem; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; + +import java.util.List; + +/** + * 购物车 + * + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +public interface ShopCartService { + + /** + * 根据购物车项id删除购物车 + * @param shopCartItemIds 购物车项id + * @param userId 用户id + */ + void deleteShopCartItemsByShopCartItemIds(Long userId,List shopCartItemIds); + + /** + * 添加购物项 + * @param userId 用户id + * @param param 购物项 + * @param priceFee 加入购物车时候的价格 + */ + void addShopCartItem(Long userId, ChangeShopCartItemDTO param, Long priceFee); + + /** + * 更新购物项 + * @param shopCartItem 购物项 + * @param userId 用户id + */ + void updateShopCartItem(Long userId, ShopCartItem shopCartItem); + + /** + * 清空用户购物车 + * @param userId 用户id + */ + void deleteAllShopCartItems(Long userId); + + /** + * 获取用户所有未过期的购物项 + * @return 未过期的购物项 + */ + List getShopCartItems(); + + /** + * 获取已过期的购物项 + * @return 已过期的购物项 + */ + List getShopCartExpiryItems(); + + /** + * 购物项数量,有缓存 + * @param userId 用户id + * @return 购物项数量 + */ + Integer getShopCartItemCount(Long userId); + + /** + * 获取被选中的购物车项 + * @return 被选中的购物车项 + */ + List getCheckedShopCartItems(); + + + /** + * 商品下架 or 变成预售的时候 移除购物车商品数量缓存 + * @param spuId + */ + void removeShopCartItemCache(Long spuId); + + /** + * 勾选购物车状态 + * @param userId 用户id + * @param params 参数 + */ + void checkShopCartItems(Long userId, List params); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuService.java new file mode 100644 index 00000000..0684b4df --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuService.java @@ -0,0 +1,86 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.vo.app.SkuAppVO; + +import java.util.List; + +/** + * sku信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SkuService { + + /** + * 保存sku信息 + * @param spu 商品id + * @param skuList sku信息 + */ + void save(Long spu, List skuList); + + /** + * 更新sku信息 + * @param spuId spuId + * @param skuList sku列表信息 + */ + void update(Long spuId, List skuList); + + /** + * 根据sku信息id删除sku信息 + * @param skuId + */ + void deleteById(Long skuId); + + /** + * 根据商品id获取商品中的sku列表(将会被缓存起来) + * @param spuId id + * @return 返回sku信息 + */ + List listBySpuId(Long spuId); + + /** + * 清除sku缓存 + * @param spuId 商品id + * @param skuIds 商品skuId列表 + */ + void removeSkuCacheBySpuIdOrSkuIds(Long spuId, List skuIds); + + + /** + * 根据spuId删除sku信息 + * @param spuId 商品id + */ + void deleteBySpuId(Long spuId); + + /** + * 获取sku的所有信息 + * @param spuId 商品id + * @return sku信息 + */ + List listBySpuIdAndExtendInfo(Long spuId); + + /** + * 根据skuId获取sku信息 + * @param skuId skuId + * @return sku信息 + */ + SkuVO getSkuBySkuId(Long skuId); + + /** + * 更新sku金额或者库存信息 + * @param spuDTO + */ + void updateAmountOrStock(SpuDTO spuDTO); + + /** + * 获取商品的sku列表(仅获取启用状态) + * @param spuId + * @return + */ + List getSkuBySpuId(Long spuId); + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockLockService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockLockService.java new file mode 100644 index 00000000..cdde7b70 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockLockService.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.api.product.dto.SkuStockLockDTO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.product.model.SkuStockLock; + +import java.util.List; + +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +public interface SkuStockLockService { + + /** + * 分页获取库存锁定信息列表 + * @param pageDTO 分页参数 + * @return 库存锁定信息列表分页数据 + */ + PageVO page(PageDTO pageDTO); + + /** + * 根据库存锁定信息id获取库存锁定信息 + * + * @param id 库存锁定信息id + * @return 库存锁定信息 + */ + SkuStockLock getById(Long id); + + /** + * 保存库存锁定信息 + * @param skuStockLock 库存锁定信息 + */ + void save(SkuStockLock skuStockLock); + + /** + * 更新库存锁定信息 + * @param skuStockLock 库存锁定信息 + */ + void update(SkuStockLock skuStockLock); + + /** + * 根据库存锁定信息id删除库存锁定信息 + * @param id 库存锁定信息id + */ + void deleteById(Long id); + + /** + * 锁定库存 + * @param skuStockLocksParam 参数 + * @return 是否成功 + */ + ServerResponseEntity lock(List skuStockLocksParam); + + /** + * 根据订单号进行库存解锁 + * @param orderIds 订单ids + */ + void unlockStock(List orderIds); + + /** + * 正式锁定库存,标记为使用状态 + * @param orderIds 订单ids + */ + void markerStockUse(List orderIds); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockService.java new file mode 100644 index 00000000..3b2d680f --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SkuStockService.java @@ -0,0 +1,60 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.model.SkuStock; +import com.mall4j.cloud.product.vo.SkuStockVO; +import com.mall4j.cloud.api.product.vo.SkuVO; + +import java.util.List; + +/** + * 库存信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SkuStockService { + + /** + * 保存库存信息 + * @param skuStock 库存信息 + */ + void save(SkuStock skuStock); + + /** + * 更新库存信息 + * @param skuStock 库存信息 + */ + void update(SkuStock skuStock); + + /** + * 根据库存信息id删除库存信息 + * @param stockId + */ + void deleteById(Long stockId); + + /** + * 批量保存库存信息 + * @param skuStocks 库存信息 + */ + void saveBatch(List skuStocks); + + /** + * 根据skuIds删除库存信息 + * @param skuIds ids + */ + void deleteBySkuIds(List skuIds); + + /** + * 根据sku列表获取库存信息 + * @param skuVOList sku列表 + * @return 库存信息 + */ + List listBySkuList(List skuVOList); + + /** + * 批量更新sku库存信息 + * @param skuList + */ + void updateBatch(List skuList); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuAttrValueService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuAttrValueService.java new file mode 100644 index 00000000..d3af5139 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuAttrValueService.java @@ -0,0 +1,60 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.api.product.vo.SpuAttrValueVO; + +import java.util.List; + +/** + * 商品规格属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuAttrValueService { + + /** + * 更新商品规格属性关联信息 + * + * @param spuId id + * @param spuAttrValues 商品属性信息 + * @param spuAttrValuesDb 缓存中的商品属性信息 + */ + void update(Long spuId, List spuAttrValues, List spuAttrValuesDb); + + /** + * 批量保存 + * + * @param spuId id + * @param spuAttrValues 规格属性信息 + */ + void saveBatch(Long spuId, List spuAttrValues); + + /** + * 根据商品id删除spuId + * + * @param spuId id + */ + void deleteBySpuId(Long spuId); + + /** + * 根据属性和分类id列表删除商品属性关联信息, 并发送消息到队列 + * @param attrId + * @param attrValueId + * @param categoryIds + */ + void deleteByAttIdAndCategoryIds(Long attrId, List attrValueId, List categoryIds); + + /** + * 根据spuId获取商品属性列表 + * @param spuId + * @return + */ + List getSpuAttrsBySpuId(Long spuId); + + /** + * 批量更新商品基本属性 + * @param spuAttrValues + */ + void batchUpdateSpuAttrValue(List spuAttrValues); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuDetailService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuDetailService.java new file mode 100644 index 00000000..1b5a43ec --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuDetailService.java @@ -0,0 +1,30 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.model.SpuDetail; + +/** + * 商品详情信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuDetailService { + + /** + * 保存商品详情信息 + * @param spuDetail 商品详情信息 + */ + void save(SpuDetail spuDetail); + + /** + * 更新商品详情信息 + * @param spuDetail 商品详情信息 + */ + void update(SpuDetail spuDetail); + + /** + * 根据商品详情信息id删除商品详情信息 + * @param spuId + */ + void deleteById(Long spuId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuExtensionService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuExtensionService.java new file mode 100644 index 00000000..e481f488 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuExtensionService.java @@ -0,0 +1,56 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.product.model.SpuExtension; +import com.mall4j.cloud.product.vo.SpuExtensionVO; + +/** + * + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public interface SpuExtensionService { + + /** + * 分页获取列表 + * @param pageDTO 分页参数 + * @return 列表分页数据 + */ + PageVO page(PageDTO pageDTO); + + /** + * 根据id获取 + * + * @param spuExtendId id + * @return + */ + SpuExtensionVO getBySpuExtendId(Long spuExtendId); + + /** + * 保存 + * @param spuExtension 商品扩展信息 + */ + void save(SpuExtension spuExtension); + + /** + * 更新库存 + * @param spuId 商品id + * @param count 商品数量 + */ + void updateStock(Long spuId, Integer count); + + /** + * 根据id删除 + * @param spuId + */ + void deleteById(Long spuId); + + /** + * 获取spu扩展信息 + * @param spuId + * @return + */ + SpuExtension getBySpuId(Long spuId); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuService.java new file mode 100644 index 00000000..f767c938 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuService.java @@ -0,0 +1,152 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.dto.SpuPageSearchDTO; +import com.mall4j.cloud.product.model.SpuExtension; + +import java.util.List; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuService { + + /** + * 分页获取spu信息列表 + * @param pageDTO 分页参数 + * @param spuDTO + * @return spu信息列表分页数据 + */ + PageVO page(PageDTO pageDTO, SpuPageSearchDTO spuDTO); + + /** + * spu基本信息 + * + * @param spuId spu信息id + * @return spu信息 + */ + SpuVO getBySpuId(Long spuId); + + /** + * spu扩展信息 + * + * @param spuId spu信息id + * @return spu信息 + */ + SpuExtension getSpuExtension(Long spuId); + + /** + * 保存spu信息 + * @param spuDTO spu信息 + */ + void save(SpuDTO spuDTO); + + /** + * 更新spu信息 + * @param spuDTO spu信息 + */ + void update(SpuDTO spuDTO); + + /** + * 根据spu信息id删除spu信息 + * @param spuId + */ + void deleteById(Long spuId); + + /** + * 根据spuId清除缓存 + * @param spuId id + */ + void removeSpuCacheBySpuId(Long spuId); + + /** + * 根据spuId列表, 批量清除缓存 + * @param spuIds id + */ + void batchRemoveSpuCacheBySpuId(List spuIds); + + /** + * 改变商品状态(上下架) + * @param spuId + * @param status + */ + void changeSpuStatus(Long spuId, Integer status); + + /** + * 更新商品的信息 + * @param spuDTO + */ + void updateSpuOrSku(SpuDTO spuDTO); + + /** + * 更新spu表(canal监听后,会发送更新的消息,更新es中的数据) + * @param spuIds + * @param categoryIds + */ + void updateSpuUpdateTime(List spuIds, List categoryIds); + + /** + * 根据spuId获取商品信息 + * @param spuId + * @return 商品信息 + */ + EsProductBO loadEsProductBO(Long spuId); + + /** + * 获取 spuId列表 + * @param shopCategoryIds 店铺分类id列表 + * @param categoryIds 平台分类Id列表 + * @param brandId 品牌id + * @param shopId 店铺id + * @return spuId列表 + */ + List getSpuIdsBySpuUpdateDTO(List shopCategoryIds, List categoryIds, Long brandId, Long shopId); + + /** + * 根据spuId列表,获取spu列表 + * @param spuIds + * @param prodName + * @param isFailure + * @return spu列表 + */ + List listBySpuIds(List spuIds, String prodName, Integer isFailure); + + /** + * 获取平台分页数据 + * @param pageDTO + * @param spuDTO + * @return + */ + PageVO platformPage(PageDTO pageDTO, SpuPageSearchDTO spuDTO); + + /** + * u + * @param spuId + * @return + */ + SpuVO getSpuDetailData(Long spuId); + + /** + * 根据分组id和是否为当前分组,返回商品列表 + * @param pageDTO 分页信息 + * @param spuDTO 分组信息 + * @param isContain 是否当前分组商品 1.参与 0.不参与 + * @return 商品列表 + */ + PageVO pageByLangAndTagId(PageDTO pageDTO, SpuDTO spuDTO, Integer isContain); + + /** + * 批量修改商品状态 + * @param cidList + * @param shopId + * @param status + */ + void batchChangeSpuStatusByCids(List cidList, Long shopId, Integer status); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuSkuAttrValueService.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuSkuAttrValueService.java new file mode 100644 index 00000000..9aaad676 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/SpuSkuAttrValueService.java @@ -0,0 +1,56 @@ +package com.mall4j.cloud.product.service; + +import com.mall4j.cloud.product.model.SpuSkuAttrValue; + +import java.util.List; + +/** + * 商品sku销售属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public interface SpuSkuAttrValueService { + + /** + * 保存商品sku销售属性关联信息 + * + * @param spuSkuAttrValue 商品sku销售属性关联信息 + */ + void save(SpuSkuAttrValue spuSkuAttrValue); + + /** + * 批量更新商品sku销售属性关联信息 + * + * @param spuSkuAttrValues 商品sku销售属性关联信息 + */ + void updateBatch(List spuSkuAttrValues); + + /** + * 根据商品sku销售属性关联信息id删除商品sku销售属性关联信息 + * + * @param spuSkuAttrId + */ + void deleteById(Long spuSkuAttrId); + + /** + * 批量保存sku规格信息 + * + * @param spuSkuAttrValues attrList + */ + void saveBatch(List spuSkuAttrValues); + + /** + * 根据spuId删除sku信息 + * + * @param spuId spuId + */ + void updateBySpuId(Long spuId); + + /** + * 根据skuId列表,改变销售属性状态 + * @param skuIds + * @param status + */ + void changeStatusBySkuId(List skuIds, Integer status); +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrCategoryServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrCategoryServiceImpl.java new file mode 100644 index 00000000..51bca946 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrCategoryServiceImpl.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.product.mapper.AttrCategoryMapper; +import com.mall4j.cloud.product.service.AttrCategoryService; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; + +/** + * 属性与属性分组关联信息 + * + * @author YXF + * @date 2020-11-23 16:20:01 + */ +@Service +public class AttrCategoryServiceImpl implements AttrCategoryService { + + @Autowired + private AttrCategoryMapper attrCategoryMapper; + + @Override + public void save(Long attrId, List categoryIds) { + attrCategoryMapper.saveBatch(attrId, categoryIds); + } + + @Override + public List update(Long attrId, List categoryIds) { + if (CollUtil.isEmpty(categoryIds)) { + return new ArrayList<>(); + } + List dbCategoryIds = attrCategoryMapper.getCategoryIdsByAttrId(attrId); + List addList = new ArrayList<>(categoryIds.size()); + addList.addAll(categoryIds); + addList.removeAll(dbCategoryIds); + if (CollUtil.isNotEmpty(addList)) { + attrCategoryMapper.saveBatch(attrId, addList); + } + dbCategoryIds.removeAll(categoryIds); + if (CollUtil.isNotEmpty(dbCategoryIds)) { + attrCategoryMapper.deleteBatch(attrId, dbCategoryIds); + } + return dbCategoryIds; + } + + @Override + public List listByAttrId(Long attrId) { + return attrCategoryMapper.listByAttrId(attrId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrServiceImpl.java new file mode 100644 index 00000000..79b74139 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrServiceImpl.java @@ -0,0 +1,153 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.RedisUtil; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageAdapter; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.constant.AttrType; +import com.mall4j.cloud.product.dto.AttrDTO; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.product.mapper.AttrMapper; +import com.mall4j.cloud.product.service.*; +import com.mall4j.cloud.api.product.vo.AttrVO; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 属性信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:23 + */ +@Service +public class AttrServiceImpl implements AttrService { + + @Autowired + private AttrMapper attrMapper; + @Autowired + private AttrCategoryService attrCategoryService; + @Autowired + private AttrValueService attrValueService; + @Autowired + private CategoryService categoryService; + @Autowired + private SpuAttrValueService spuAttrValueService; + + @Override + public PageVO page(PageDTO pageDTO, AttrDTO attrDTO) { + PageVO pageVO = new PageVO<>(); + attrDTO.setShopId(AuthUserContext.get().getTenantId()); + pageVO.setList(attrMapper.list(new PageAdapter(pageDTO), attrDTO)); + pageVO.setTotal(attrMapper.countAttr(attrDTO)); + pageVO.setPages(PageUtil.getPages(pageVO.getTotal(), pageDTO.getPageSize())); + return pageVO; + } + + @Override + public AttrVO getByAttrId(Long attrId) { + AttrVO attrVO = attrMapper.getByAttrId(attrId); + if (Objects.isNull(attrVO)) { + throw new mall4cloudException("属性不存在或已删除"); + } + if (Objects.equals(attrVO.getAttrType(), AttrType.BASIC.value())) { + attrVO.setCategories(attrCategoryService.listByAttrId(attrId)); + categoryService.getPathNames(attrVO.getCategories()); + } + return attrVO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(Attr attr, List categoryIds) { + attr.setShopId(AuthUserContext.get().getTenantId()); + attrMapper.save(attr); + // 保存属性值 + attrValueService.saveByAttrValuesAndAttrId(attr.getAttrValues(), attr.getAttrId()); + // 基本属性关联分类 + if (Objects.equals(AttrType.BASIC.value(), attr.getAttrType())) { + // 保存属性分类关联信息 + attrCategoryService.save(attr.getAttrId(), categoryIds); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Attr attr, List categoryIds) { + AttrVO dbAttr = attrMapper.getByAttrId(attr.getAttrId()); + // 属性名、描述相等,则设为null,不进行修改操作 + if (Objects.equals(attr.getName(), dbAttr.getName())) { + attr.setName(null); + } + if (Objects.equals(attr.getDesc(), dbAttr.getDesc())) { + attr.setDesc(null); + } + // 保存属性值 + attrValueService.update(attr, dbAttr); + attrMapper.update(attr); + if (Objects.equals(dbAttr.getAttrType(), AttrType.BASIC.value())) { + // 更新属性分类关联信息 + List ids = attrCategoryService.update(attr.getAttrId(), categoryIds); + // 清除取消关联的分类的数据 + spuAttrValueService.deleteByAttIdAndCategoryIds(attr.getAttrId(), null, ids); + } + } + + @Override + public void deleteById(Long attrId) { + AttrVO dbAttr = getByAttrId(attrId); + if (Objects.isNull(dbAttr)) { + throw new mall4cloudException("该属性已删除或不存在"); + } + if (Objects.equals(dbAttr.getAttrType(), AttrType.BASIC.value())) { + List categoryIds = dbAttr.getCategories().stream().map(CategoryVO::getCategoryId).collect(Collectors.toList()); + spuAttrValueService.deleteByAttIdAndCategoryIds(attrId, null, categoryIds); + } + attrMapper.deleteById(attrId); + } + + @Override + @Cacheable(cacheNames = CacheNames.ATTRS_BY_CATEGORY_KEY, key = "#categoryId") + public List getAttrsByCategoryIdAndAttrType(Long categoryId) { + return attrMapper.getAttrsByCategoryIdAndAttrType(categoryId); + } + + @Override + public List getAttrOfCategoryIdByAttrId(Long attrId) { + AttrVO attr = attrMapper.getByAttrId(attrId); + if (Objects.isNull(attr)) { + throw new mall4cloudException("属性不存在"); + } + if (CollUtil.isEmpty(attr.getCategories())) { + return new ArrayList<>(); + } + return attr.getCategories().stream().map(CategoryVO::getCategoryId).collect(Collectors.toList()); + } + + @Override + public void removeAttrByCategoryId(List categoryIds) { + if (CollUtil.isEmpty(categoryIds)) { + return; + } + List keys = new ArrayList<>(); + for (Long categoryId : categoryIds) { + keys.add(CacheNames.ATTRS_BY_CATEGORY_KEY + CacheNames.UNION + categoryId); + } + RedisUtil.deleteBatch(keys); + } + + @Override + public List getShopAttrs(Long shopId) { + return attrMapper.getShopAttrs(shopId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrValueServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrValueServiceImpl.java new file mode 100644 index 00000000..c995de2b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/AttrValueServiceImpl.java @@ -0,0 +1,120 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.product.constant.AttrType; +import com.mall4j.cloud.product.mapper.AttrMapper; +import com.mall4j.cloud.product.model.Attr; +import com.mall4j.cloud.product.model.AttrValue; +import com.mall4j.cloud.product.mapper.AttrValueMapper; +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.product.service.AttrValueService; +import com.mall4j.cloud.api.product.vo.AttrVO; +import com.mall4j.cloud.api.product.vo.AttrValueVO; +import com.mall4j.cloud.product.service.SpuAttrValueService; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 属性值信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class AttrValueServiceImpl implements AttrValueService { + + @Autowired + private AttrValueMapper attrValueMapper; + + @Autowired + private AttrMapper attrMapper; + + @Autowired + private SpuAttrValueService spuAttrValueService; + + @Override + public void saveByAttrValuesAndAttrId(List attrValues, Long attrId) { + if (CollUtil.isEmpty(attrValues)){ + return; + } + for (AttrValue attrValue : attrValues) { + attrValue.setAttrId(attrId); + } + attrValueMapper.saveBatch(attrValues); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Attr attr, AttrVO dbAttr) { + List addAttrValue = new ArrayList<>(); + List updateAttrValue = new ArrayList<>(); + List deleteAttrValue = dbAttr.getAttrValues().stream().map(AttrValueVO::getAttrValueId).collect(Collectors.toList()); + // 分别筛选出增删改的数据 + attr.getAttrValues().forEach(attrValue -> { + if (Objects.isNull(attrValue.getAttrValueId())) { + addAttrValue.add(attrValue); + } else { + updateAttrValue.add(attrValue); + deleteAttrValue.remove(attrValue.getAttrValueId()); + } + }); + if (CollUtil.isNotEmpty(deleteAttrValue)) { + if (Objects.equals(attr.getAttrType(), AttrType.BASIC.value())) { + spuAttrValueService.deleteByAttIdAndCategoryIds(attr.getAttrId(), deleteAttrValue, null); + } + attrValueMapper.deleteBatch(deleteAttrValue); + } + + // 新增属性值数据 + saveByAttrValuesAndAttrId(addAttrValue, attr.getAttrId()); + if (CollUtil.isNotEmpty(updateAttrValue)) { + attrValueMapper.updateBatch(updateAttrValue); + } + if (Objects.equals(attr.getAttrType(), AttrType.BASIC.value())) { + updateAttrAndAttrValueOfSpuOrSku(updateAttrValue, attr, dbAttr); + } + } + + /** + * 更新属性数据时,更新商品/sku中的属性数据 + * 若不需要同步更新商品/sku中的属性数据,在更新属性数据时,不调用该方法即可,不影响其他流程 + * @param updateAttrValue + * @param dbAttr + */ + private void updateAttrAndAttrValueOfSpuOrSku(List updateAttrValue, Attr attr, AttrVO dbAttr) { + if (CollUtil.isEmpty(updateAttrValue)) { + return; + } + Map dbAttrValueMap = dbAttr.getAttrValues().stream().collect(Collectors.toMap(AttrValueVO::getAttrValueId, AttrValueVO::getValue)); + boolean attrNotChange = Objects.equals(dbAttr.getName(), dbAttrValueMap.get(attr.getName())) + && Objects.equals(dbAttr.getDesc(), dbAttrValueMap.get(attr.getDesc())); + List spuAttrValues = new ArrayList<>(updateAttrValue.size()); + for (AttrValue attrValue : updateAttrValue) { + boolean valueNotChange = Objects.equals(attrValue.getValue(), dbAttrValueMap.get(attrValue.getAttrValueId())); + // 数据没有改变或者仅改变了描述,则跳过本次循环 + if (attrNotChange && valueNotChange) { + continue; + } else if (Objects.isNull(attr.getName()) && valueNotChange) { + continue; + } + + SpuAttrValue spuAttrValue = new SpuAttrValue(); + spuAttrValue.setAttrName(attr.getName()); + spuAttrValue.setAttrDesc(attr.getDesc()); + spuAttrValue.setAttrValueName(attrValue.getValue()); + spuAttrValue.setAttrId(attr.getAttrId()); + spuAttrValue.setAttrValueId(attrValue.getAttrValueId()); + spuAttrValues.add(spuAttrValue); + } + // 更新数据 + if(CollUtil.isNotEmpty(spuAttrValues)) { + spuAttrValueService.batchUpdateSpuAttrValue(spuAttrValues); + } + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/BrandServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/BrandServiceImpl.java new file mode 100644 index 00000000..bd762ee7 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/BrandServiceImpl.java @@ -0,0 +1,116 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.RedisUtil; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.product.dto.BrandDTO; +import com.mall4j.cloud.product.model.Brand; +import com.mall4j.cloud.product.mapper.BrandMapper; +import com.mall4j.cloud.product.service.BrandService; +import com.mall4j.cloud.product.service.CategoryBrandService; +import com.mall4j.cloud.api.product.vo.BrandVO; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 品牌信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class BrandServiceImpl implements BrandService { + + @Autowired + private BrandMapper brandMapper; + @Autowired + private CategoryBrandService categoryBrandService; + + @Override + public PageVO page(PageDTO pageDTO, BrandDTO brandDTO) { + return PageUtil.doPage(pageDTO, () -> brandMapper.list(brandDTO)); + } + + @Override + public BrandVO getByBrandId(Long brandId) { + return brandMapper.getByBrandId(brandId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(Brand brand, List categoryIds) { + brand.setFirstLetter(brand.getFirstLetter().toUpperCase()); + brand.setStatus(StatusEnum.ENABLE.value()); + brandMapper.save(brand); + categoryBrandService.saveByCategoryIds(brand.getBrandId(), categoryIds); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Brand brand, List categoryIds) { + brandMapper.update(brand); + categoryBrandService.updateByCategoryIds(brand.getBrandId(), categoryIds); + } + + @Override + public void deleteById(Long brandId) { + if (getUseNum(brandId) > 0){ + throw new mall4cloudException("有部分商品在使用该品牌,不能进行删除操作"); + } + brandMapper.deleteById(brandId); + categoryBrandService.deleteByBrandId(brandId); + } + + @Override + public List getBrandByCategoryId(Long categoryId) { + return brandMapper.getBrandByCategoryId(categoryId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.BRAND_TOP) + public void updateBrandStatus(BrandDTO brandDTO) { + BrandVO dbBrand = getByBrandId(brandDTO.getBrandId()); + if (Objects.isNull(dbBrand) || dbBrand.getStatus().equals(brandDTO.getStatus())) { + return; + } + brandMapper.updateBrandStatus(brandDTO); + + } + + @Override + @Cacheable(cacheNames = CacheNames.BRAND_LIST_BY_CATEGORY, key = "#categoryId") + public List listByCategory(Long categoryId) { + return brandMapper.listByCategory(categoryId); + } + + @Override + @Cacheable(cacheNames = CacheNames.BRAND_TOP) + public List topBrandList() { + return brandMapper.topBrandList(); + } + + @Override + @CacheEvict(cacheNames = CacheNames.BRAND_TOP) + public void removeCache(List categoryIds) { + if (CollUtil.isEmpty(categoryIds)) { + return; + } + Set categoryIdSet = new HashSet<>(categoryIds); + RedisUtil.deleteBatch(CacheNames.BRAND_LIST_BY_CATEGORY, new ArrayList<>(categoryIdSet)); + } + + private int getUseNum(Long brandId) { + return brandMapper.getUseNum(brandId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryBrandServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryBrandServiceImpl.java new file mode 100644 index 00000000..2436e57b --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryBrandServiceImpl.java @@ -0,0 +1,71 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.product.model.CategoryBrand; +import com.mall4j.cloud.product.mapper.CategoryBrandMapper; +import com.mall4j.cloud.product.service.CategoryBrandService; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; + +/** + * 品牌分类关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class CategoryBrandServiceImpl implements CategoryBrandService { + + @Autowired + private CategoryBrandMapper categoryBrandMapper; + + @Override + public void deleteByBrandId(Long brandId) { + categoryBrandMapper.deleteByBrandId(brandId); + } + + @Override + public void saveByCategoryIds(Long brandId, List categoryIds) { + if (CollUtil.isEmpty(categoryIds)) { + return; + } + List categoryBrandList = new ArrayList<>(); + categoryIds.forEach(categoryId -> { + CategoryBrand categoryBrand = new CategoryBrand(); + categoryBrand.setBrandId(brandId); + categoryBrand.setCategoryId(categoryId); + categoryBrandList.add(categoryBrand); + }); + categoryBrandMapper.saveBatch(categoryBrandList); + } + + @Override + public void updateByCategoryIds(Long brandId, List categoryIds) { + if (CollUtil.isEmpty(categoryIds)) { + return; + } + List categoryIdDb = getCategoryIdBrandId(brandId); + List addList = new ArrayList<>(); + categoryIds.forEach(categoryId -> { + if (!categoryIdDb.contains(categoryId)) { + addList.add(categoryId); + } + }); + if(CollUtil.isNotEmpty(addList)) { + saveByCategoryIds(brandId, addList); + } + categoryIdDb.removeAll(categoryIds); + if (CollUtil.isNotEmpty(categoryIdDb)) { + categoryBrandMapper.deleteByBrandIdAndCategoryIds(brandId, categoryIdDb); + } + } + + @Override + public List getCategoryIdBrandId(Long brandId) { + return categoryBrandMapper.getCategoryIdsByBrandId(brandId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryServiceImpl.java new file mode 100644 index 00000000..291f12bd --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/CategoryServiceImpl.java @@ -0,0 +1,263 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.api.product.constant.CategoryLevel; +import com.mall4j.cloud.product.dto.CategoryDTO; +import com.mall4j.cloud.product.model.Category; +import com.mall4j.cloud.product.mapper.CategoryMapper; +import com.mall4j.cloud.product.service.CategoryService; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.product.service.SpuService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 分类信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class CategoryServiceImpl implements CategoryService { + + @Autowired + private CategoryMapper categoryMapper; + + @Autowired + private SpuService spuService; + + @Override + public CategoryVO getById(Long categoryId) { + CategoryVO category = categoryMapper.getById(categoryId); + if (Objects.isNull(category)) { +// throw new mall4cloudException("分类不存在,请刷新后重试"); + return category; + } + List paths = new ArrayList<>(); + paths.add(category); + getPathNames(paths); + return category; + } + + @Override + public void save(Category category) { + checkName(null, category.getName()); + category.setShopId(AuthUserContext.get().getTenantId()); + String path = ""; + if (!Objects.equals(CategoryLevel.First.value(), category.getLevel())) { + String parentId = String.valueOf(category.getParentId()); + CategoryVO categoryDb = categoryMapper.getById(category.getParentId()); + if (StrUtil.isBlank(categoryDb.getPath())) { + path = parentId; + } else { + path = categoryDb.getPath() + Constant.CATEGORY_INTERVAL + parentId; + } + } + category.setPath(path); + categoryMapper.save(category); + } + + @Override + public void update(Category category) { + CategoryVO dbCategory = getById(category.getCategoryId()); + if (Objects.equals(dbCategory.getCategoryId(), category.getParentId())) { + throw new mall4cloudException("分类不能成为本身的上级分类"); + } + checkName(category.getCategoryId(), category.getName()); + int updateCount = categoryMapper.update(category); + } + + @Override + public void deleteById(Long categoryId) { + int count = categoryMapper.getCategoryUseNum(categoryId); + if (count > 0) { + throw new mall4cloudException("该分类在使用中,不能进行删除操作"); + } + categoryMapper.deleteById(categoryId); + } + + @Override + public List list(Long shopId) { + return categoryMapper.list(shopId); + } + + @Override + public List listCategoryId(Long shopId, Long parentId) { + return categoryMapper.listCategoryId(shopId, parentId); + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.CATEGORY_LIST_BY_PARENT_ID_AND_SHOP_ID, key = "#shopId + ':' + #parentId"), + @CacheEvict(cacheNames = CacheNames.CATEGORY_LIST_ALL_OF_SHOP, key = "#shopId"), + @CacheEvict(cacheNames = CacheNames.CATEGORY_LIST, key = "#shopId + ':' + #parentId") + }) + public void removeCategoryCache(Long shopId, Long parentId) { + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean categoryEnableOrDisable(CategoryDTO categoryDTO) { + CategoryVO categoryDb = getById(categoryDTO.getCategoryId()); + // 如果是重复提交,则直接返回 + if (Objects.equals(categoryDb.getStatus(), categoryDTO.getStatus())) { + return Boolean.TRUE; + } + List updateList = new ArrayList<>(); + List thirdIdList = new ArrayList<>(); + if (!categoryDb.getLevel().equals(CategoryLevel.THIRD.value())) { + // 如果是店铺的二级分类需要将分类id放进去 + if (!Objects.equals(categoryDb.getShopId(), Constant.PLATFORM_SHOP_ID) && Objects.equals(categoryDb.getLevel(), CategoryLevel.SECOND.value())) { + thirdIdList.add(categoryDb.getCategoryId()); + } + + List categoryList = categoryMapper.getChildCategory(categoryDb.getCategoryId()); + categoryList.forEach(category -> { + updateList.add(category.getCategoryId()); + if (Objects.equals(categoryDb.getShopId(), Constant.PLATFORM_SHOP_ID) && Objects.equals(category.getLevel(), CategoryLevel.THIRD.value())) { + thirdIdList.add(category.getCategoryId()); + } else if (!Objects.equals(categoryDb.getShopId(), Constant.PLATFORM_SHOP_ID) && Objects.equals(category.getLevel(), CategoryLevel.SECOND.value())) { + thirdIdList.add(category.getCategoryId()); + } + }); + } else { + updateList.add(categoryDb.getCategoryId()); + thirdIdList.add(categoryDb.getCategoryId()); + } + updateList.add(categoryDb.getCategoryId()); + categoryMapper.updateBatchOfStatus(updateList, categoryDTO.getStatus()); + + // 分类下架后,下架分类中的商品 + if (Objects.equals(categoryDTO.getStatus(), StatusEnum.DISABLE.value())) { + if (CollUtil.isEmpty(thirdIdList)) { + return Boolean.TRUE; + } + spuService.batchChangeSpuStatusByCids(thirdIdList, categoryDb.getShopId(), StatusEnum.DISABLE.value()); + } + return Boolean.TRUE; + } + + @Override + public void getPathNames(List categorys) { + if (CollUtil.isEmpty(categorys)) { + return; + } + // 获取分类的所有上级分类id集合 + Set paths = new HashSet<>(); + for (CategoryVO category : categorys) { + if (Objects.isNull(category) || StrUtil.isBlank(category.getPath())) { + continue; + } + String[] parentIds = category.getPath().split(Constant.CATEGORY_INTERVAL); + for (String parentId : parentIds) { + paths.add(Long.valueOf(parentId)); + } + } + if (CollUtil.isEmpty(paths)) { + return; + } + + // 获取所有上级分类id列表 + List listByCategoryIds = categoryMapper.getListByCategoryIds(paths); + Map categoryMap = listByCategoryIds.stream().collect(Collectors.toMap(Category::getCategoryId, c -> c)); + + // 获取每个分类的上级分类名称集合 + for (CategoryVO category : categorys) { + if (StrUtil.isBlank(category.getPath())) { + continue; + } + String[] parentIdArray = category.getPath().split(Constant.CATEGORY_INTERVAL); + category.setPathNames(new ArrayList<>()); + for (int i = 0; i < parentIdArray.length; i++) { + String pathName = categoryMap.get(Long.valueOf(parentIdArray[i])).getName(); + category.getPathNames().add(pathName); + } + } + } + + @Override + public CategoryVO getPathNameByCategoryId(Long categoryId) { + CategoryVO category = getById(categoryId); + if (Objects.isNull(category)) { + return null; + } + List categorys = new ArrayList<>(1); + categorys.add(category); + getPathNames(categorys); + return category; + } + + @Override + @Cacheable(cacheNames = CacheNames.CATEGORY_LIST, key = "#shopId + ':' + #parentId") + public List categoryList(Long shopId, Long parentId) { + if (!Objects.equals(shopId, Constant.PLATFORM_SHOP_ID) || Objects.equals(parentId, Constant.CATEGORY_ID)) { + return categoryMapper.listByShopIdAndParenId(shopId, parentId); + } + List categoryVOList = categoryMapper.getCategoryAndChildCatogory(shopId, parentId); + List categoryList = categoryVOList.stream().filter(c->c.getParentId().equals(parentId)) + .collect(Collectors.toList()); + setChildCategory(categoryList, categoryVOList); + return categoryList; + } + + @Override + @Cacheable(cacheNames = CacheNames.CATEGORY_LIST_BY_PARENT_ID_AND_SHOP_ID, key = "#shopId + ':' + #parentId") + public List listByShopIdAndParenId(Long shopId, Long parentId) { + return categoryMapper.listByShopIdAndParenId(shopId, parentId); + } + + @Override + @Cacheable(cacheNames = CacheNames.CATEGORY_LIST_ALL_OF_SHOP, key = "#shopId") + public List shopCategoryList(Long shopId) { + List list = categoryMapper.listByShopIdAndParenId(shopId, null); + Map> categoryMap = list.stream().collect(Collectors.groupingBy(CategoryVO::getLevel)); + + List secondCategories = categoryMap.get(CategoryLevel.SECOND.value()); + if (Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) { + // 三级分类 + List thirdCategories = categoryMap.get(CategoryLevel.THIRD.value()); + //二级分类 + setChildCategory(secondCategories, thirdCategories); + } + //一级分类 + List firstCategories = categoryMap.get(CategoryLevel.First.value()); + setChildCategory(firstCategories, secondCategories); + return firstCategories; + } + + private void setChildCategory(List categories, List childCategories) { + if (CollUtil.isEmpty(categories) || CollUtil.isEmpty(childCategories)) { + return; + } + Map> secondCategoryMap = childCategories.stream().collect(Collectors.groupingBy(CategoryVO::getParentId)); + for (CategoryVO category : categories) { + category.setCategories(secondCategoryMap.get(category.getCategoryId())); + } + } + + /** + * 校验分类名是否已存在 + * + * @param categoryId + * @param name + */ + private void checkName(Long categoryId, String name) { + int countByName = categoryMapper.getCountByName(categoryId, name); + if (countByName > 0) { + throw new mall4cloudException("分类名已存在,请重新输入"); + } + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/ShopCartServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/ShopCartServiceImpl.java new file mode 100644 index 00000000..409f57f1 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/ShopCartServiceImpl.java @@ -0,0 +1,115 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.CacheManagerUtil; +import com.mall4j.cloud.common.order.vo.ShopCartItemVO; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.dto.shopcart.ChangeShopCartItemDTO; +import com.mall4j.cloud.product.dto.shopcart.CheckShopCartItemDTO; +import com.mall4j.cloud.product.mapper.ShopCartItemMapper; +import com.mall4j.cloud.product.model.ShopCartItem; +import com.mall4j.cloud.product.service.ShopCartService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 购物车 + * + * @author FrozenWatermelon + * @date 2020-11-20 15:47:32 + */ +@Service +public class ShopCartServiceImpl implements ShopCartService { + + @Autowired + private ShopCartItemMapper shopCartItemMapper; + + @Autowired + private CacheManagerUtil cacheManagerUtil; + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_CART_ITEM_COUNT, key = "#userId") + public void deleteShopCartItemsByShopCartItemIds(Long userId,List shopCartItemIds) { + shopCartItemMapper.deleteShopCartItemsByShopCartItemIds(userId, shopCartItemIds); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_CART_ITEM_COUNT, key = "#userId") + public void addShopCartItem(Long userId, ChangeShopCartItemDTO param, Long priceFee) { + ShopCartItem shopCartItem = new ShopCartItem(); + shopCartItem.setCount(param.getCount()); + shopCartItem.setSpuId(param.getSpuId()); + shopCartItem.setShopId(param.getShopId()); + shopCartItem.setUserId(userId); + shopCartItem.setSkuId(param.getSkuId()); + shopCartItem.setIsChecked(1); + shopCartItem.setPriceFee(priceFee); + shopCartItemMapper.save(shopCartItem); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_CART_ITEM_COUNT, key = "#userId") + public void updateShopCartItem(Long userId,ShopCartItem shopCartItem) { + shopCartItemMapper.update(shopCartItem); + } + + @Override + @CacheEvict(cacheNames = CacheNames.SHOP_CART_ITEM_COUNT, key = "#userId") + public void deleteAllShopCartItems(Long userId) { + shopCartItemMapper.deleteAllShopCartItems(userId); + } + + @Override + public List getShopCartItems() { + Long userId = AuthUserContext.get().getUserId(); + List shopCartItems = shopCartItemMapper.getShopCartItems(userId, false, null); + for (ShopCartItemVO shopCartItem : shopCartItems) { + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + } + return shopCartItems; + } + + @Override + public List getShopCartExpiryItems() { + Long userId = AuthUserContext.get().getUserId(); + List shopCartItems = shopCartItemMapper.getShopCartItems(userId, true, null); + for (ShopCartItemVO shopCartItem : shopCartItems) { + shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee()); + } + return shopCartItems; + } + + @Override + @Cacheable(cacheNames = CacheNames.SHOP_CART_ITEM_COUNT, key = "#userId") + public Integer getShopCartItemCount(Long userId) { + return shopCartItemMapper.getShopCartItemCount(userId); + } + + @Override + public List getCheckedShopCartItems() { + Long userId = AuthUserContext.get().getUserId(); + return shopCartItemMapper.getShopCartItems(userId, false, true); + } + + @Override + public void removeShopCartItemCache(Long spuId) { + List userIds = shopCartItemMapper.listUserIdBySpuId(spuId); + if (CollectionUtil.isEmpty(userIds)) { + return; + } + for (String userId : userIds) { + cacheManagerUtil.evictCache(CacheNames.SHOP_CART_ITEM_COUNT, userId); + } + } + + @Override + public void checkShopCartItems(Long userId, List params) { + shopCartItemMapper.checkShopCartItems(userId, params); + } + +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuServiceImpl.java new file mode 100644 index 00000000..67f2eb22 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuServiceImpl.java @@ -0,0 +1,211 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.product.vo.SpuSkuAttrValueVO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.RedisUtil; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.mapper.SkuMapper; +import com.mall4j.cloud.product.model.Sku; +import com.mall4j.cloud.product.model.SkuStock; +import com.mall4j.cloud.product.model.SpuSkuAttrValue; +import com.mall4j.cloud.product.service.SkuService; +import com.mall4j.cloud.product.service.SkuStockService; +import com.mall4j.cloud.product.service.SpuSkuAttrValueService; +import com.mall4j.cloud.api.product.vo.SkuVO; +import com.mall4j.cloud.product.vo.app.SkuAppVO; +import ma.glasnost.orika.MapperFacade; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * sku信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SkuServiceImpl implements SkuService { + + @Autowired + private SkuMapper skuMapper; + @Autowired + private SpuSkuAttrValueService spuSkuAttrValueService; + @Autowired + private SkuStockService skuStockService; + @Autowired + private MapperFacade mapperFacade; + + @Override + public void save(Long spuId, List skuList) { + skuList.forEach(sku -> { + sku.setSpuId(spuId); + sku.setStatus(StatusEnum.ENABLE.value()); + }); + // 处理数据,保存库存、属性 + skuMapper.saveBatch(skuList); + List skuStocks = new ArrayList<>(); + List spuSkuAttrValues = new ArrayList<>(); + for (SkuDTO skuDTO : skuList) { + SkuStock skuStock = new SkuStock(); + skuStock.setSkuId(skuDTO.getSkuId()); + skuStock.setStock(skuDTO.getStock()); + skuStock.setActualStock(skuDTO.getStock()); + skuStock.setLockStock(0); + skuStocks.add(skuStock); + List spuSkuAttrValueList = mapperFacade.mapAsList(skuDTO.getSpuSkuAttrValues(), SpuSkuAttrValue.class); + for (SpuSkuAttrValue spuSkuAttrValue : spuSkuAttrValueList) { + spuSkuAttrValue.setSpuId(spuId); + spuSkuAttrValue.setSkuId(skuDTO.getSkuId()); + spuSkuAttrValue.setStatus(StatusEnum.ENABLE.value()); + spuSkuAttrValues.add(spuSkuAttrValue); + } + } + skuStockService.saveBatch(skuStocks); + spuSkuAttrValueService.saveBatch(spuSkuAttrValues); + } + + + @Override + public void update(Long spuId, List skuList) { + // 获取当前商品所有的sku + List skuListDb = skuMapper.listBySpuId(spuId); + List skuIdsDb = skuListDb.stream().map(SkuVO::getSkuId).collect(Collectors.toList()); + List skuIds = new ArrayList<>(); + List updateSkus = new ArrayList<>(); + List insertSkus = new ArrayList<>(); + for (SkuDTO sku : skuList) { + sku.setSpuId(spuId); + if (skuIdsDb.contains(sku.getSkuId())) { + updateSkus.add(sku); + skuIds.add(sku.getSkuId()); + } else if (Objects.isNull(sku.getSkuId())){ + insertSkus.add(sku); + } + } + // 新增的sku--保存 + if(CollUtil.isNotEmpty(insertSkus)) { + save(spuId,insertSkus); + } + // 已有的sku--更新 + if(CollUtil.isNotEmpty(updateSkus)){ + List skus = mapperFacade.mapAsList(updateSkus, Sku.class); + skuMapper.updateBatch(skus); + skuStockService.updateBatch(updateSkus); + } + // 不存在的sku--删除 + skuIdsDb.removeAll(skuIds); + if (CollUtil.isNotEmpty(skuIdsDb)) { + deleteSkuBatch(skuIdsDb); + } + } + + @Override + public void deleteById(Long skuId) { + skuMapper.deleteById(skuId); + } + + @Override + @Cacheable(cacheNames = CacheNames.SKU_LIST_KEY, key = "#spuId",sync = true) + public List listBySpuId(Long spuId) { + return skuMapper.listBySpuId(spuId); + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.SKU_LIST_KEY, key = "#spuId"), + @CacheEvict(cacheNames = CacheNames.SKU_OF_SPU_DETAIL_KEY, key = "#spuId") + }) + public void removeSkuCacheBySpuIdOrSkuIds(Long spuId, List skuIds) { + // 根据spuId删除缓存 + if (CollUtil.isEmpty(skuIds)) { + // 获取当前类的代理,这样就可以利用spring的方法获取缓存了,不要删了 + SkuServiceImpl skuService = (SkuServiceImpl) AopContext.currentProxy(); + List skuList = skuService.listBySpuId(spuId); + skuIds = skuList.stream().map(SkuVO::getSkuId).collect(Collectors.toList()); + } + RedisUtil.deleteBatch(CacheNames.SKU_BY_ID_KEY, skuIds); + } + + @Override + public void deleteBySpuId(Long spuId) { + skuMapper.updateBySpuId(spuId); + } + + @Override + public List listBySpuIdAndExtendInfo(Long spuId) { + return skuMapper.listBySpuIdAndExtendInfo(spuId); + } + + @Override + @Cacheable(cacheNames = CacheNames.SKU_BY_ID_KEY, key = "#skuId") + public SkuVO getSkuBySkuId(Long skuId) { + return skuMapper.getSkuBySkuId(skuId); + } + + @Override + public void updateAmountOrStock(SpuDTO spuDTO) { + List skuList = spuDTO.getSkuList(); + List skus = new ArrayList<>(); + Boolean isUpdateStock = false; + for (SkuDTO skuDTO : skuList) { + if (Objects.nonNull(skuDTO.getChangeStock()) && skuDTO.getChangeStock() > 0) { + isUpdateStock = true; + break; + } else if (Objects.nonNull(skuDTO.getPriceFee())) { + Sku sku = new Sku(); + sku.setSkuId(skuDTO.getSkuId()); + sku.setPriceFee(skuDTO.getPriceFee()); + skus.add(sku); + } + } + if (isUpdateStock) { + skuStockService.updateBatch(skuList); + } + if (CollUtil.isNotEmpty(skus)) { + skuMapper.updateBatch(skus); + } + } + + @Override + @Cacheable(cacheNames = CacheNames.SKU_OF_SPU_DETAIL_KEY, key = "#spuId",sync = true) + public List getSkuBySpuId(Long spuId) { + String attrUnionAttrValue = ":"; + String attrUnionAttr = ";"; + List skuAppList = new ArrayList<>(); + List skuData = skuMapper.getSkuBySpuId(spuId); + for (SkuVO sku : skuData) { + SkuAppVO skuAppVO = mapperFacade.map(sku, SkuAppVO.class); + String properties = ""; + for (SpuSkuAttrValueVO spuSkuAttrValue : sku.getSpuSkuAttrValues()) { + properties = properties + spuSkuAttrValue.getAttrName() + attrUnionAttrValue + spuSkuAttrValue.getAttrValueName() + attrUnionAttr; + } + skuAppVO.setProperties(properties.substring(0, properties.length()-1)); + skuAppList.add(skuAppVO); + } + return skuAppList; + } + + private void deleteSkuBatch(List skuIds) { + List skus = new ArrayList<>(); + for (Long skuId : skuIds) { + Sku sku = new Sku(); + sku.setSkuId(skuId); + sku.setStatus(StatusEnum.DELETE.value()); + skus.add(sku); + } + skuMapper.updateBatch(skus); + spuSkuAttrValueService.changeStatusBySkuId(skuIds, StatusEnum.DELETE.value()); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockLockServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockLockServiceImpl.java new file mode 100644 index 00000000..2980b315 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockLockServiceImpl.java @@ -0,0 +1,182 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.api.order.bo.OrderStatusBO; +import com.mall4j.cloud.api.order.constant.OrderStatus; +import com.mall4j.cloud.api.order.feign.OrderFeignClient; +import com.mall4j.cloud.api.product.dto.SkuStockLockDTO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import com.mall4j.cloud.product.bo.SkuWithStockBO; +import com.mall4j.cloud.product.mapper.SkuStockLockMapper; +import com.mall4j.cloud.product.mapper.SkuStockMapper; +import com.mall4j.cloud.product.mapper.SpuExtensionMapper; +import com.mall4j.cloud.product.model.SkuStockLock; +import com.mall4j.cloud.product.service.SkuStockLockService; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 库存锁定信息 + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +@Service +public class SkuStockLockServiceImpl implements SkuStockLockService { + + @Autowired + private SkuStockLockMapper skuStockLockMapper; + + @Autowired + private SpuExtensionMapper spuExtensionMapper; + + @Autowired + private SkuStockMapper skuStockMapper; + + + @Autowired + private OrderFeignClient orderFeignClient; + + @Autowired + private RocketMQTemplate stockMqTemplate; + + @Override + public PageVO page(PageDTO pageDTO) { + return PageUtil.doPage(pageDTO, () -> skuStockLockMapper.list()); + } + + @Override + public SkuStockLock getById(Long id) { + return skuStockLockMapper.getById(id); + } + + @Override + public void save(SkuStockLock skuStockLock) { + skuStockLockMapper.save(skuStockLock); + } + + @Override + public void update(SkuStockLock skuStockLock) { + skuStockLockMapper.update(skuStockLock); + } + + @Override + public void deleteById(Long id) { + skuStockLockMapper.deleteById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ServerResponseEntity lock(List skuStockLocksParam) { + + List skuStockLocks = new ArrayList<>(); + for (SkuStockLockDTO skuStockLockDTO : skuStockLocksParam) { + SkuStockLock skuStockLock = new SkuStockLock(); + skuStockLock.setCount(skuStockLockDTO.getCount()); + skuStockLock.setOrderId(skuStockLockDTO.getOrderId()); + skuStockLock.setSkuId(skuStockLockDTO.getSkuId()); + skuStockLock.setSpuId(skuStockLockDTO.getSpuId()); + skuStockLock.setStatus(0); + skuStockLocks.add(skuStockLock); + // 减sku库存 + int skuStockUpdateIsSuccess = skuStockMapper.reduceStockByOrder(skuStockLockDTO.getSkuId(), skuStockLockDTO.getCount()); + if (skuStockUpdateIsSuccess < 1) { + throw new mall4cloudException(ResponseEnum.NOT_STOCK, skuStockLockDTO.getSkuId()); + } + // 减商品库存 + int spuStockUpdateIsSuccess = spuExtensionMapper.reduceStockByOrder(skuStockLockDTO.getSpuId(), skuStockLockDTO.getCount()); + if (spuStockUpdateIsSuccess < 1) { + throw new mall4cloudException(ResponseEnum.NOT_STOCK, skuStockLockDTO.getSkuId()); + } + } + // 保存库存锁定信息 + skuStockLockMapper.saveBatch(skuStockLocks); + List orderIds = skuStockLocksParam.stream().map(SkuStockLockDTO::getOrderId).collect(Collectors.toList()); + // 一个小时后解锁库存 + SendStatus sendStatus = stockMqTemplate.syncSend(RocketMqConstant.STOCK_UNLOCK_TOPIC, new GenericMessage<>(orderIds), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL + 1).getSendStatus(); + if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + return ServerResponseEntity.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void unlockStock(List orderIds) { + ServerResponseEntity> ordersStatusResponse = orderFeignClient.getOrdersStatus(orderIds); + if (!ordersStatusResponse.isSuccess()) { + throw new mall4cloudException(ordersStatusResponse.getMsg()); + } + List orderStatusList = ordersStatusResponse.getData(); + + List needUnLockOrderId = new ArrayList<>(); + for (OrderStatusBO orderStatusBO : orderStatusList) { + // 该订单没有下单成功,或订单已取消,赶紧解锁库存 + if (orderStatusBO.getStatus() == null || Objects.equals(orderStatusBO.getStatus(), OrderStatus.CLOSE.value())) { + needUnLockOrderId.add(orderStatusBO.getOrderId()); + } + } + + if (CollectionUtil.isEmpty(needUnLockOrderId)) { + return; + } + + List allSkuWithStocks = skuStockLockMapper.listByOrderIds(orderIds); + if (CollectionUtil.isEmpty(allSkuWithStocks)) { + return; + } + List lockIds = allSkuWithStocks.stream().map(SkuWithStockBO::getId).collect(Collectors.toList()); + + // 还原商品库存 + spuExtensionMapper.addStockByOrder(allSkuWithStocks); + // 还原sku库存 + skuStockMapper.addStockByOrder(allSkuWithStocks); + // 将锁定状态标记为已解锁 + skuStockLockMapper.unLockByIds(lockIds); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void markerStockUse(List orderIds) { + + List skuWithStocks = skuStockLockMapper.listByOrderIds(orderIds); + + // ==============订单从正常状态变成已支付============= + if (CollectionUtil.isNotEmpty(skuWithStocks)) { + // 减少商品实际库存,增加销量 + spuExtensionMapper.reduceActualStockByOrder(skuWithStocks); + // 减少sku实际库存 + skuStockMapper.reduceActualStockByOrder(skuWithStocks); + } + + // ================ 由于订单支付回调成功过慢,导致订单由取消变成已支付 ==================== + + List unLockSkuWithStocks = skuStockLockMapper.listUnLockByOrderIds(orderIds); + + if (CollectionUtil.isNotEmpty(unLockSkuWithStocks)) { + // 减少商品实际库存,增加销量 + spuExtensionMapper.reduceActualStockByCancelOrder(unLockSkuWithStocks); + // 减少sku实际库存 + skuStockMapper.reduceActualStockByCancelOrder(unLockSkuWithStocks); + } + // 将锁定状态标记为已使用 + skuStockLockMapper.markerStockUse(orderIds); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockServiceImpl.java new file mode 100644 index 00000000..478f1a6c --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SkuStockServiceImpl.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.product.dto.SkuDTO; +import com.mall4j.cloud.product.mapper.SkuStockMapper; +import com.mall4j.cloud.product.model.SkuStock; +import com.mall4j.cloud.product.service.SkuStockService; +import com.mall4j.cloud.product.vo.SkuStockVO; +import com.mall4j.cloud.api.product.vo.SkuVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * 库存信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SkuStockServiceImpl implements SkuStockService { + + @Autowired + private SkuStockMapper skuStockMapper; + + @Override + public void save(SkuStock skuStock) { + skuStockMapper.save(skuStock); + } + + @Override + public void update(SkuStock skuStock) { + skuStockMapper.update(skuStock); + } + + @Override + public void deleteById(Long stockId) { + skuStockMapper.deleteById(stockId); + } + + @Override + public void saveBatch(List skuStocks) { + skuStockMapper.saveBatch(skuStocks); + } + + @Override + public void deleteBySkuIds(List skuIds) { + skuStockMapper.deleteBySkuIds(skuIds); + } + + @Override + public List listBySkuList(List skuVOList) { + return skuStockMapper.listBySkuList(skuVOList); + } + + @Override + public void updateBatch(List skuList) { + if (CollUtil.isEmpty(skuList)) { + return; + } + // 如果是修改库存,此时不需要改变锁定库存 + List skuStocks = new ArrayList<>(); + for (SkuDTO sku : skuList) { + SkuStock skuStock = new SkuStock(); + if (Objects.nonNull(sku.getChangeStock()) && sku.getChangeStock() > 0) { + skuStock.setStock(sku.getChangeStock()); + skuStock.setSkuId(sku.getSkuId()); + skuStocks.add(skuStock); + } + } + if (CollUtil.isNotEmpty(skuStocks)) { + skuStockMapper.updateStock(skuStocks); + } + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuAttrValueServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuAttrValueServiceImpl.java new file mode 100644 index 00000000..bc11ca1d --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuAttrValueServiceImpl.java @@ -0,0 +1,116 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.product.mapper.SpuAttrValueMapper; +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.product.service.SpuAttrValueService; +import com.mall4j.cloud.api.product.vo.SpuAttrValueVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 商品规格属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SpuAttrValueServiceImpl implements SpuAttrValueService { + + @Autowired + private SpuAttrValueMapper spuAttrValueMapper; + @Autowired + private SpuServiceImpl spuService; + + @Override + public void update(Long spuId, List spuAttrValues, List spuAttrValuesDb) { + List spuAttrValueIdsDb = spuAttrValuesDb.stream().map(SpuAttrValueVO::getSpuAttrValueId).collect(Collectors.toList()); + List updateList = new ArrayList<>(); + List saveList = new ArrayList<>(); + List spuAttrValueIds = new ArrayList<>(); + for (SpuAttrValue spuAttrValue : spuAttrValues) { + if (spuAttrValueIdsDb.contains(spuAttrValue.getSpuAttrValueId())) { + if (Objects.nonNull(spuAttrValue.getAttrValueName()) || Objects.nonNull(spuAttrValue.getAttrValueId())) { + updateList.add(spuAttrValue); + } + spuAttrValueIds.add(spuAttrValue.getSpuAttrValueId()); + continue; + } + spuAttrValue.setSpuId(spuId); + saveList.add(spuAttrValue); + } + // 保存新增的关联属性 + if (CollUtil.isNotEmpty(saveList)) { + saveBatch(spuId, saveList); + } + // 更新属性 + if (CollUtil.isNotEmpty(updateList)) { + spuAttrValueMapper.updateBatch(updateList); + } + // 删除属性 + spuAttrValueIdsDb.removeAll(spuAttrValueIds); + if (CollUtil.isNotEmpty(spuAttrValueIdsDb)) { + spuAttrValueMapper.deleteBatch(spuAttrValueIdsDb); + } + } + + @Override + public void saveBatch(Long spuId, List spuAttrValues) { + if (CollUtil.isEmpty(spuAttrValues)) { + return; + } + for (SpuAttrValue spuAttrValue : spuAttrValues) { + spuAttrValue.setSpuId(spuId); + } + spuAttrValueMapper.saveBatch(spuAttrValues); + } + + @Override + public void deleteBySpuId(Long spuId) { + spuAttrValueMapper.deleteBySpuId(spuId); + } + + @Override + public void deleteByAttIdAndCategoryIds(Long attrId, List attrValueId, List categoryIds) { + if (CollUtil.isEmpty(attrValueId) && CollUtil.isEmpty(categoryIds) ) { + return; + } + updateSpu(attrValueId, categoryIds); + spuAttrValueMapper.deleteByAttIdAndCategoryIds(attrId, attrValueId, categoryIds); + } + + @Override + public List getSpuAttrsBySpuId(Long spuId) { + return spuAttrValueMapper.getSpuAttrsBySpuId(spuId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void batchUpdateSpuAttrValue(List spuAttrValues) { + spuAttrValueMapper.batchUpdateSpuAttrValue(spuAttrValues); + List attrValueIds = spuAttrValues.stream().map(SpuAttrValue::getAttrValueId).collect(Collectors.toList()); + updateSpu(attrValueIds, null); + } + + + private void updateSpu(List attrValueIds, List categoryIds) { + List spuIds = null; + if (CollUtil.isNotEmpty(attrValueIds)) { + spuIds = spuAttrValueMapper.getShopIdByAttrValueIds(attrValueIds); + spuService.updateSpuUpdateTime(spuIds, null); + } else if (CollUtil.isNotEmpty(categoryIds)) { + spuIds = spuService.getSpuIdsBySpuUpdateDTO(null, categoryIds, null, null); + spuService.updateSpuUpdateTime(null, categoryIds); + } + if (CollUtil.isEmpty(spuIds)) { + return ; + } + spuService.batchRemoveSpuCacheBySpuId(spuIds); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuDetailServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuDetailServiceImpl.java new file mode 100644 index 00000000..0d474607 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuDetailServiceImpl.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.product.service.impl; + +import com.mall4j.cloud.product.mapper.SpuDetailMapper; +import com.mall4j.cloud.product.model.SpuDetail; +import com.mall4j.cloud.product.service.SpuDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 商品详情信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SpuDetailServiceImpl implements SpuDetailService { + + @Autowired + private SpuDetailMapper spuDetailMapper; + + @Override + public void save(SpuDetail spuDetail) { + spuDetailMapper.save(spuDetail); + } + + @Override + public void update(SpuDetail spuDetail) { + spuDetailMapper.update(spuDetail); + } + + @Override + public void deleteById(Long spuId) { + spuDetailMapper.deleteById(spuId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuExtensionServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuExtensionServiceImpl.java new file mode 100644 index 00000000..b9eab664 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuExtensionServiceImpl.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.product.service.impl; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.product.mapper.SpuExtensionMapper; +import com.mall4j.cloud.product.model.SpuExtension; +import com.mall4j.cloud.product.service.SpuExtensionService; +import com.mall4j.cloud.product.vo.SpuExtensionVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +@Service +public class SpuExtensionServiceImpl implements SpuExtensionService { + + @Autowired + private SpuExtensionMapper spuExtensionMapper; + + @Override + public PageVO page(PageDTO pageDTO) { + return PageUtil.doPage(pageDTO, () -> spuExtensionMapper.list()); + } + + @Override + public SpuExtensionVO getBySpuExtendId(Long spuExtendId) { + return spuExtensionMapper.getBySpuExtendId(spuExtendId); + } + + @Override + public void save(SpuExtension spuExtension) { + spuExtensionMapper.save(spuExtension); + } + + @Override + public void updateStock(Long spuId, Integer count) { + spuExtensionMapper.updateStock(spuId,count); + } + + @Override + public void deleteById(Long spuId) { + spuExtensionMapper.deleteById(spuId); + } + + @Override + public SpuExtension getBySpuId(Long spuId) { + return spuExtensionMapper.getBySpuId(spuId); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuServiceImpl.java new file mode 100644 index 00000000..166edb3f --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuServiceImpl.java @@ -0,0 +1,318 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.multishop.feign.IndexImgFeignClient; +import com.mall4j.cloud.api.multishop.feign.ShopDetailFeignClient; +import com.mall4j.cloud.api.multishop.vo.ShopDetailVO; +import com.mall4j.cloud.api.product.bo.EsAttrBO; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.vo.CategoryVO; +import com.mall4j.cloud.api.product.vo.SpuAttrValueVO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.RedisUtil; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.product.dto.SpuDTO; +import com.mall4j.cloud.product.dto.SpuPageSearchDTO; +import com.mall4j.cloud.product.mapper.SpuMapper; +import com.mall4j.cloud.product.service.*; +import com.mall4j.cloud.api.product.vo.SpuVO; +import com.mall4j.cloud.product.model.Spu; +import com.mall4j.cloud.product.model.SpuAttrValue; +import com.mall4j.cloud.product.model.SpuDetail; +import com.mall4j.cloud.product.model.SpuExtension; +import io.seata.spring.annotation.GlobalTransactional; +import ma.glasnost.orika.MapperFacade; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * spu信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SpuServiceImpl implements SpuService { + + @Autowired + private SpuMapper spuMapper; + + @Autowired + private SpuDetailService spuDetailService; + + @Autowired + private SpuExtensionService spuExtensionService; + + @Autowired + private SpuAttrValueService spuAttrValueService; + + @Autowired + private MapperFacade mapperFacade; + + @Autowired + private SkuService skuService; + + @Autowired + private BrandService brandService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private ShopDetailFeignClient shopDetailFeignClient; + + @Autowired + private IndexImgFeignClient indexImgFeignClient; + + @Override + public PageVO page(PageDTO pageDTO, SpuPageSearchDTO spuDTO) { + PageVO spuPage = PageUtil.doPage(pageDTO, () -> spuMapper.list(spuDTO)); + return spuPage; + } + + @Override + @Cacheable(cacheNames = CacheNames.SPU_KEY, key = "#spuId",sync = true) + public SpuVO getBySpuId(Long spuId) { + return spuMapper.getBySpuId(spuId); + } + + @Override + @Cacheable(cacheNames = CacheNames.SPU_EXTENSION_KEY, key = "#spuId",sync = true) + public SpuExtension getSpuExtension(Long spuId) { + SpuExtension spuExtension = spuExtensionService.getBySpuId(spuId); + return spuExtension; + } + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.SPU_KEY, key = "#spuId"), + @CacheEvict(cacheNames = CacheNames.SPU_EXTENSION_KEY, key = "#spuId") + }) + public void removeSpuCacheBySpuId(Long spuId) { + } + + @Override + public void batchRemoveSpuCacheBySpuId(List spuIds) { + RedisUtil.deleteBatch(CacheNames.SPU_KEY, spuIds); + RedisUtil.deleteBatch(CacheNames.SPU_EXTENSION_KEY, spuIds); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public void changeSpuStatus(Long spuId, Integer status) { + spuMapper.changeSpuStatus(spuId, status); + if (!Objects.equals(status, StatusEnum.ENABLE)) { + SpuVO spuVO = spuMapper.getBySpuId(spuId); + ServerResponseEntity imgRes = indexImgFeignClient.deleteBySpuId(spuVO.getSpuId(), spuVO.getShopId()); + if (!imgRes.isSuccess()) { + throw new mall4cloudException("服务异常"); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SpuDTO spuDTO) { + Spu spu = mapperFacade.map(spuDTO, Spu.class); + spu.setShopId(AuthUserContext.get().getTenantId()); + spu.setStatus(StatusEnum.ENABLE.value()); + spu.setShopId(AuthUserContext.get().getTenantId()); + // 1.保存商品信息 + spuMapper.save(spu); + // 2.保存商品其他信息,规格、详细、扩展信息 + List spuAttrValues = mapperFacade.mapAsList(spuDTO.getSpuAttrValues(), SpuAttrValue.class); + for (SpuAttrValue spuAttrValue : spuAttrValues) { + if (Objects.isNull(spuAttrValue.getAttrValueId())) { + spuAttrValue.setAttrValueId(0L); + } + } + spuAttrValueService.saveBatch(spu.getSpuId(),spuAttrValues); + SpuDetail spuDetail = new SpuDetail(); + spuDetail.setSpuId(spu.getSpuId()); + spuDetail.setDetail(spuDTO.getDetail()); + spuDetailService.save(spuDetail); + + SpuExtension spuExtension = new SpuExtension(); + spuExtension.setSpuId(spu.getSpuId()); + spuExtension.setActualStock(spuDTO.getTotalStock()); + spuExtension.setStock(spuDTO.getTotalStock()); + spuExtensionService.save(spuExtension); + + // 3.保存sku信息 + skuService.save(spu.getSpuId(),spuDTO.getSkuList()); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SpuDTO spuDTO) { + Spu spu = mapperFacade.map(spuDTO, Spu.class); + SpuServiceImpl spuService = (SpuServiceImpl) AopContext.currentProxy(); + SpuVO dbSpu = spuService.getBySpuId(spu.getSpuId()); + // 1.修改商品信息 + spuMapper.update(spu); + List spuAttrValues = mapperFacade.mapAsList(spuDTO.getSpuAttrValues(), SpuAttrValue.class); + spuAttrValueService.update(spu.getSpuId(),spuAttrValues, dbSpu.getSpuAttrValues()); + + // 2.修改商品详情 + SpuDetail spuDetail = new SpuDetail(); + spuDetail.setSpuId(spu.getSpuId()); + spuDetail.setDetail(spuDTO.getDetail()); + spuDetailService.update(spuDetail); + + // 3.修改商品库存 + if (Objects.nonNull(spuDTO.getChangeStock()) && spuDTO.getChangeStock() > 0) { + SpuExtension spuExtension = new SpuExtension(); + spuExtension.setSpuId(spu.getSpuId()); + spuExtension.setStock(spuDTO.getChangeStock()); + spuExtensionService.updateStock(spu.getSpuId(), spuDTO.getTotalStock()); + } + + // 4.修改商品sku信息 + skuService.update(spu.getSpuId(),spuDTO.getSkuList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @GlobalTransactional(rollbackFor = Exception.class) + public void deleteById(Long spuId) { + SpuVO spuVO = getBySpuId(spuId); + if(Objects.isNull(spuVO) || Objects.equals(spuVO.getStatus(), StatusEnum.DELETE.value())){ + throw new mall4cloudException("商品不存在或者已被删除!"); + } + // 删除商品、sku信息(逻辑删除) + spuMapper.updateStatusBySpuId(spuId); + skuService.deleteBySpuId(spuId); + // 把轮播图中关联了该商品的数据删除 + ServerResponseEntity imgRes = indexImgFeignClient.deleteBySpuId(spuId, spuVO.getShopId()); + if (!imgRes.isSuccess()) { + throw new mall4cloudException("服务异常"); + } + } + + @Override + public void updateSpuOrSku(SpuDTO spuDTO) { + Spu spu = new Spu(); + spu.setSpuId(spuDTO.getSpuId()); + spu.setName(spuDTO.getName()); + spu.setSeq(spuDTO.getSeq()); + if (CollUtil.isNotEmpty(spuDTO.getSkuList())) { + skuService.updateAmountOrStock(spuDTO); + } + if (Objects.nonNull(spuDTO.getChangeStock()) && spuDTO.getChangeStock() > 0) { + spuExtensionService.updateStock(spuDTO.getSpuId(), spuDTO.getChangeStock()); + return; + } + spu.setPriceFee(spuDTO.getPriceFee()); + spuMapper.update(spu); + } + + @Override + public void updateSpuUpdateTime(List spuIds, List categoryIds) { + if (CollUtil.isEmpty(spuIds) && CollUtil.isEmpty(categoryIds)) { + return; + } + spuMapper.updateSpuUpdateTime(spuIds, categoryIds); + } + + @Override + public EsProductBO loadEsProductBO(Long spuId) { + // 获取商品、品牌数据 + EsProductBO esProductBO = spuMapper.loadEsProductBO(spuId); + // 获取分类数据 + CategoryVO category = categoryService.getPathNameByCategoryId(esProductBO.getCategoryId()); + String[] categoryIdArray = category.getPath().split(Constant.CATEGORY_INTERVAL); + esProductBO.setCategoryName(category.getName()); + for (int i = 0;i spuAttrsBySpuId = spuAttrValueService.getSpuAttrsBySpuId(spuId); + List attrs = spuAttrsBySpuId.stream().filter(spuAttrValueVO -> spuAttrValueVO.getSearchType().equals(1)).collect(Collectors.toList()); + esProductBO.setAttrs(mapperFacade.mapAsList(attrs, EsAttrBO.class)); + return esProductBO; + } + + @Override + public List getSpuIdsBySpuUpdateDTO(List shopCategoryIds, List categoryIds, Long brandId, Long shopId) { + if (CollUtil.isEmpty(shopCategoryIds) && CollUtil.isEmpty(categoryIds) && Objects.isNull(brandId) && Objects.isNull(shopId)) { + return new ArrayList<>(); + } + return spuMapper.getSpuIdsBySpuUpdateDTO(shopCategoryIds, categoryIds, brandId, shopId); + } + + @Override + public List listBySpuIds(List spuIds, String prodName, Integer isFailure) { + if (CollUtil.isEmpty(spuIds)) { + return new ArrayList<>(); + } + return spuMapper.listBySpuIds(spuIds,prodName,isFailure); + } + + @Override + public PageVO platformPage(PageDTO pageDTO, SpuPageSearchDTO spuDTO) { + PageVO page = page(pageDTO, spuDTO); + Set shopIdSet = page.getList().stream().map(SpuVO::getShopId).collect(Collectors.toSet()); + ServerResponseEntity> shopResponse = shopDetailFeignClient.listByShopIds(new ArrayList<>(shopIdSet)); + Map shopMap = shopResponse.getData().stream().collect(Collectors.toMap(ShopDetailVO::getShopId, s -> s)); + for (SpuVO spuVO : page.getList()) { + ShopDetailVO shopDetailVO = shopMap.get(spuVO.getShopId()); + if (Objects.isNull(shopDetailVO)) { + continue; + } + spuVO.setShopName(shopDetailVO.getShopName()); + } + return page; + } + + @Override + public SpuVO getSpuDetailData(Long spuId) { + return null; + } + + @Override + public PageVO pageByLangAndTagId(PageDTO pageDTO, SpuDTO spuDTO, Integer isContain) { + return PageUtil.doPage(pageDTO, () -> spuMapper.pageByLangAndTagId(pageDTO,spuDTO,isContain)); + } + + @Override + public void batchChangeSpuStatusByCids(List cidList, Long shopId, Integer status) { + List spuIdList; + if (Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) { + spuIdList = spuMapper.listIdsByCidListAndTypeAndShopId(cidList, 1, shopId); + } else { + spuIdList = spuMapper.listIdsByCidListAndTypeAndShopId(cidList, 2, shopId); + } + if (Objects.isNull(spuIdList) || spuIdList.size() == 0) { + return; + } + spuMapper.batchChangeSpuStatusBySpuIdsAndStatus(spuIdList, status); + this.batchRemoveSpuCacheBySpuId(spuIdList); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuSkuAttrValueServiceImpl.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuSkuAttrValueServiceImpl.java new file mode 100644 index 00000000..7c13ca7f --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/service/impl/SpuSkuAttrValueServiceImpl.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.product.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.product.mapper.SpuSkuAttrValueMapper; +import com.mall4j.cloud.product.model.SpuSkuAttrValue; +import com.mall4j.cloud.product.service.SpuSkuAttrValueService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 商品sku销售属性关联信息 + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +@Service +public class SpuSkuAttrValueServiceImpl implements SpuSkuAttrValueService { + + @Autowired + private SpuSkuAttrValueMapper spuSkuAttrValueMapper; + + @Override + public void save(SpuSkuAttrValue spuSkuAttrValue) { + spuSkuAttrValueMapper.save(spuSkuAttrValue); + } + + @Override + public void updateBatch(List spuSkuAttrValues) { + if (CollUtil.isNotEmpty(spuSkuAttrValues)) { + spuSkuAttrValueMapper.updateBatch(spuSkuAttrValues); + } + } + + @Override + public void deleteById(Long spuSkuAttrId) { + spuSkuAttrValueMapper.deleteById(spuSkuAttrId); + } + + @Override + public void saveBatch(List spuSkuAttrValues) { + if (CollUtil.isEmpty(spuSkuAttrValues)) { + return; + } + spuSkuAttrValueMapper.saveBatch(spuSkuAttrValues); + } + + @Override + public void updateBySpuId(Long spuId) { + spuSkuAttrValueMapper.updateBySpuId(spuId); + } + + @Override + public void changeStatusBySkuId(List skuIds, Integer status) { + spuSkuAttrValueMapper.changeStatusBySkuId(skuIds, status); + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/CategoryBrandVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/CategoryBrandVO.java new file mode 100644 index 00000000..c121f95d --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/CategoryBrandVO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 品牌分类关联信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class CategoryBrandVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty() + private Long id; + + @ApiModelProperty("品牌id") + private Long brandId; + + @ApiModelProperty("分类id") + private Long categoryId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + @Override + public String toString() { + return "CategoryBrandVO{" + + "id=" + id + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",brandId=" + brandId + + ",categoryId=" + categoryId + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/ShopCartAmountVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/ShopCartAmountVO.java new file mode 100644 index 00000000..ed2af050 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/ShopCartAmountVO.java @@ -0,0 +1,65 @@ +package com.mall4j.cloud.product.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + */ +@ApiModel("购物车合计") +public class ShopCartAmountVO { + + @ApiModelProperty("总额") + private Long totalMoney; + + @ApiModelProperty("总计") + private Long finalMoney; + + @ApiModelProperty("减额") + private Long subtractMoney; + + @ApiModelProperty("商品数量") + private Integer count; + + public Long getTotalMoney() { + return totalMoney; + } + + public void setTotalMoney(Long totalMoney) { + this.totalMoney = totalMoney; + } + + public Long getFinalMoney() { + return finalMoney; + } + + public void setFinalMoney(Long finalMoney) { + this.finalMoney = finalMoney; + } + + public Long getSubtractMoney() { + return subtractMoney; + } + + public void setSubtractMoney(Long subtractMoney) { + this.subtractMoney = subtractMoney; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + @Override + public String toString() { + return "ShopCartAmountVO{" + + "totalMoney=" + totalMoney + + ", finalMoney=" + finalMoney + + ", subtractMoney=" + subtractMoney + + ", count=" + count + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockLockVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockLockVO.java new file mode 100644 index 00000000..18bd60c3 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockLockVO.java @@ -0,0 +1,94 @@ +package com.mall4j.cloud.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 库存锁定信息VO + * + * @author FrozenWatermelon + * @date 2020-12-22 16:12:10 + */ +public class SkuStockLockVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("sku id") + private Long skuId; + + @ApiModelProperty("订单id") + private Long orderId; + + @ApiModelProperty("锁定库存数量") + private Integer count; + + @ApiModelProperty("状态-1已解锁 0待确定 1已锁定") + private Integer status; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "SkuStockLockVO{" + + "id=" + id + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",skuId=" + skuId + + ",orderId=" + orderId + + ",count=" + count + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockVO.java new file mode 100644 index 00000000..b2e8461d --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SkuStockVO.java @@ -0,0 +1,58 @@ +package com.mall4j.cloud.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 库存信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SkuStockVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("库存id") + private Long stockId; + + @ApiModelProperty("SKU ID") + private Long skuId; + + @ApiModelProperty("库存") + private Integer stock; + + public Long getStockId() { + return stockId; + } + + public void setStockId(Long stockId) { + this.stockId = stockId; + } + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "StockVO{" + + "stockId=" + stockId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",skuId=" + skuId + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuDetailVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuDetailVO.java new file mode 100644 index 00000000..870790d9 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuDetailVO.java @@ -0,0 +1,46 @@ +package com.mall4j.cloud.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +/** + * 商品详情信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuDetailVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("商品详情") + private String detail; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + @Override + public String toString() { + return "SpuDetailVO{" + + "spuId=" + spuId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",detail=" + detail + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuExtensionVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuExtensionVO.java new file mode 100644 index 00000000..abc1946d --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/SpuExtensionVO.java @@ -0,0 +1,104 @@ +package com.mall4j.cloud.product.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; + +/** + * VO + * + * @author FrozenWatermelon + * @date 2020-11-11 13:49:06 + */ +public class SpuExtensionVO extends BaseVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("商品扩展信息表id") + private Long spuExtendId; + + @ApiModelProperty("创建时间") + private Date createTime; + + @ApiModelProperty("更新时间") + private Date updateTime; + + @ApiModelProperty("商品id") + private Long spuId; + + @ApiModelProperty("销量") + private Integer saleNum; + + @ApiModelProperty("实际库存") + private Integer actualStock; + + @ApiModelProperty("锁定库存") + private Integer lockStock; + + @ApiModelProperty("可售卖库存") + private Integer stock; + + public Long getSpuExtendId() { + return spuExtendId; + } + + public void setSpuExtendId(Long spuExtendId) { + this.spuExtendId = spuExtendId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public Integer getActualStock() { + return actualStock; + } + + public void setActualStock(Integer actualStock) { + this.actualStock = actualStock; + } + + public Integer getLockStock() { + return lockStock; + } + + public void setLockStock(Integer lockStock) { + this.lockStock = lockStock; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "SpuExtensionVO{" + + "spuExtendId=" + spuExtendId + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + ", spuId=" + spuId + + ", saleNum=" + saleNum + + ", actualStock=" + actualStock + + ", lockStock=" + lockStock + + ", stock=" + stock + + ", createTime=" + createTime + + ", updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SkuAppVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SkuAppVO.java new file mode 100644 index 00000000..67af6f14 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SkuAppVO.java @@ -0,0 +1,117 @@ +package com.mall4j.cloud.product.vo.app; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +/** + * sku信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SkuAppVO{ + + @ApiModelProperty("属性id") + private Long skuId; + + @ApiModelProperty("SPU id") + private Long spuId; + + @ApiModelProperty("sku名称") + private String skuName; + + @JsonSerialize(using = ImgJsonSerializer.class) + @ApiModelProperty("banner图片") + private String imgUrl; + + @ApiModelProperty("售价,整数方式保存") + private Long priceFee; + + @ApiModelProperty("市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty("库存") + private Integer stock; + + @ApiModelProperty("属性") + private String properties; + + public Long getSkuId() { + return skuId; + } + + public void setSkuId(Long skuId) { + this.skuId = skuId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public String getProperties() { + return properties; + } + + public void setProperties(String properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "SkuAppVO{" + + "skuId=" + skuId + + ", spuId=" + spuId + + ", skuName='" + skuName + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", stock=" + stock + + ", properties='" + properties + '\'' + + '}'; + } +} diff --git a/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SpuAppVO.java b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SpuAppVO.java new file mode 100644 index 00000000..46e10631 --- /dev/null +++ b/mall4cloud-product/src/main/java/com/mall4j/cloud/product/vo/app/SpuAppVO.java @@ -0,0 +1,181 @@ +package com.mall4j.cloud.product.vo.app; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mall4j.cloud.api.product.vo.SpuAttrValueVO; +import com.mall4j.cloud.common.serializer.ImgJsonSerializer; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * spu信息VO + * + * @author FrozenWatermelon + * @date 2020-10-28 15:27:24 + */ +public class SpuAppVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("spu id") + private Long spuId; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("spu名称") + private String name; + + @ApiModelProperty("卖点") + private String sellingPoint; + + @JsonSerialize(using = ImgJsonSerializer.class) + @ApiModelProperty("商品介绍主图") + private String mainImgUrl; + + @JsonSerialize(using = ImgJsonSerializer.class) + @ApiModelProperty("商品介绍主图 多个图片逗号分隔") + private String imgUrls; + + @ApiModelProperty("售价,整数方式保存") + private Long priceFee; + + @ApiModelProperty("市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty("商品详情") + private String detail; + + @ApiModelProperty("总库存") + private Integer totalStock; + + @ApiModelProperty("规格属性") + private List spuAttrValues; + + @ApiModelProperty("sku列表") + private List skus; + + @ApiModelProperty("商品销量") + private Integer saleNum; + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public List getSkus() { + return skus; + } + + public void setSkus(List skus) { + this.skus = skus; + } + + public List getSpuAttrValues() { + return spuAttrValues; + } + + public void setSpuAttrValues(List spuAttrValues) { + this.spuAttrValues = spuAttrValues; + } + + public Integer getTotalStock() { + return totalStock; + } + + public void setTotalStock(Integer totalStock) { + this.totalStock = totalStock; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImgUrls() { + return imgUrls; + } + + public void setImgUrls(String imgUrls) { + this.imgUrls = imgUrls; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + @Override + public String toString() { + return "SpuVO{" + + "spuId=" + spuId + + ", name='" + name + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", imgUrls='" + imgUrls + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", detail='" + detail + '\'' + + ", totalStock=" + totalStock + + ", spuAttrValues=" + spuAttrValues + + ", skus=" + skus + + ", saleNum=" + saleNum + + '}'; + } +} diff --git a/mall4cloud-product/src/main/resources/bootstrap.yml b/mall4cloud-product/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..1083e47e --- /dev/null +++ b/mall4cloud-product/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9104 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-product/src/main/resources/mapper/AttrCategoryMapper.xml b/mall4cloud-product/src/main/resources/mapper/AttrCategoryMapper.xml new file mode 100644 index 00000000..7c6734df --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/AttrCategoryMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + insert into attr_category (`category_id`,`attr_id`) values (#{categoryId},#{attrId}); + + + + delete from attr_category where attr_id = #{attrId} and category_id IN + + #{categoryId} + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/AttrMapper.xml b/mall4cloud-product/src/main/resources/mapper/AttrMapper.xml new file mode 100644 index 00000000..6fa324f6 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/AttrMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + `attr_id`,`shop_id`,`create_time`,`update_time`,`name`,`desc`,`search_type`,`attr_type` + + + + + + insert into attr (`name`,`desc`,`search_type`,`attr_type`,`shop_id`) + values (#{attr.name},#{attr.desc},#{attr.searchType},#{attr.attrType},#{attr.shopId}) + + + update attr + set + + `name` = #{attr.name}, + + + `desc` = #{attr.desc}, + + + `search_type` = #{attr.searchType}, + + update_time = NOW() + where attr_id = #{attr.attrId} + + + delete from attr where attr_id = #{attrId}; + delete from attr_value where attr_id = #{attrId}; + delete from attr_category where attr_id = #{attrId}; + + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/AttrValueMapper.xml b/mall4cloud-product/src/main/resources/mapper/AttrValueMapper.xml new file mode 100644 index 00000000..3a03ecf8 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/AttrValueMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + `attr_value_id`,`create_time`,`update_time`,`attr_id`,`value` + + + + insert into attr_value (`attr_id`,`value`) + values + + (#{attrValue.attrId},#{attrValue.value}) + + + + + + + + update attr_value + + + `value` = #{attrValue.value} + + + where attr_value_id = #{attrValue.attrValueId}; + + + + + DELETE FROM attr_value WHERE attr_value_id IN + + #{attrValueId} + + + + + + UPDATE + spu_attr_value + + + `attr_name` = #{spuAttrValue.attrName}, + + + `attr_value_name` = #{spuAttrValue.attrValueName}, + + + WHERE `attr_id` = #{spuAttrValue.attrId} AND `attr_value_id` = #{spuAttrValue.attrValueId}; + + + + + + UPDATE + spu_sku_attr_value + + + `attr_name` = #{spuSkuAttrValue.attrName}, + + + `attr_value_name` = #{spuSkuAttrValue.attrValueName}, + + + WHERE `attr_id` = #{spuSkuAttrValue.attrId} AND `attr_value_id` = #{spuSkuAttrValue.attrValueId}; + + + diff --git a/mall4cloud-product/src/main/resources/mapper/BrandMapper.xml b/mall4cloud-product/src/main/resources/mapper/BrandMapper.xml new file mode 100644 index 00000000..e653612a --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/BrandMapper.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `brand_id`,`create_time`,`update_time`,`name`,`desc`,`img_url`,`first_letter`,`seq`,`status` + + + + + insert into brand (`name`,`desc`,`img_url`,`first_letter`,`seq`,`status`) + values (#{brand.name},#{brand.desc},#{brand.imgUrl},#{brand.firstLetter},#{brand.seq},#{brand.status}); + + + update brand + set + + `name` = #{brand.name}, + + + `desc` = #{brand.desc}, + + + `img_url` = #{brand.imgUrl}, + + + `first_letter` = #{brand.firstLetter}, + + + `seq` = #{brand.seq}, + + + `status` = #{brand.status}, + + update_time = NOW() + where brand_id = #{brand.brandId} + + + delete from brand where brand_id = #{brandId} + + + + + + + + UPDATE brand SET `status` = #{brand.status} WHERE `brand_id` = #{brand.brandId}; + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/CategoryBrandMapper.xml b/mall4cloud-product/src/main/resources/mapper/CategoryBrandMapper.xml new file mode 100644 index 00000000..bf02d3a3 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/CategoryBrandMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + `id`,`create_time`,`update_time`,`brand_id`,`category_id` + + + delete from category_brand where brand_id = #{brandId} + + + + + insert into category_brand (`brand_id`,`category_id`) + values (#{categoryBrand.brandId},#{categoryBrand.categoryId}); + + + + + + + DELETE FROM category_brand + WHERE brand_id = #{brandId} AND category_id IN + + #{categoryId} + + + diff --git a/mall4cloud-product/src/main/resources/mapper/CategoryMapper.xml b/mall4cloud-product/src/main/resources/mapper/CategoryMapper.xml new file mode 100644 index 00000000..c4ae48ef --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/CategoryMapper.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + `category_id`,`create_time`,`update_time`,`shop_id`,`parent_id`,`name`,`desc`,`path`,`status`,`icon`,`img_url`,`level`,`seq` + + + + insert into category (`shop_id`,`parent_id`,`name`,`desc`,`path`,`status`,`icon`,`img_url`,`level`,`seq`) + values (#{category.shopId},#{category.parentId},#{category.name},#{category.desc},#{category.path},#{category.status},#{category.icon},#{category.imgUrl},#{category.level},#{category.seq}); + + + update category + + + `name` = #{category.name}, + + + `desc` = #{category.desc}, + + + `icon` = #{category.icon}, + + + `img_url` = #{category.imgUrl}, + + + `seq` = #{category.seq}, + + + where category_id = #{category.categoryId} + + + delete from category where category_id = #{categoryId} + + + + + + + + + + + + UPDATE category SET `status` = #{status} WHERE `category_id` in + + #{categoryId} + + + + + + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/ShopCartItemMapper.xml b/mall4cloud-product/src/main/resources/mapper/ShopCartItemMapper.xml new file mode 100644 index 00000000..276e2023 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/ShopCartItemMapper.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + `cart_item_id`, + `create_time`, + `update_time`, + `shop_id`, + `spu_id`, + `sku_id`, + `user_id`, + `count`, + `price_fee`, + `distribution_user_id`, + is_checked + + + + + insert into shop_cart_item (`shop_id`, `spu_id`, `sku_id`, `user_id`, `count`, `price_fee`, is_checked) + values (#{shopCartItem.shopId}, #{shopCartItem.spuId}, #{shopCartItem.skuId}, #{shopCartItem.userId}, + #{shopCartItem.count}, #{shopCartItem.priceFee}, #{shopCartItem.isChecked}); + + + update shop_cart_item + + + `shop_id` = #{shopCartItem.shopId}, + + + `spu_id` = #{shopCartItem.spuId}, + + + `sku_id` = #{shopCartItem.skuId}, + + + `user_id` = #{shopCartItem.userId}, + + + `count` = #{shopCartItem.count}, + + + `price_fee` = #{shopCartItem.priceFee}, + + + `is_checked` = #{shopCartItem.isChecked}, + + + where cart_item_id = #{shopCartItem.cartItemId} and user_id =#{shopCartItem.userId} + + + delete + from shop_cart_item + where cart_item_id = #{cartItemId} + + + + + + + delete + from shop_cart_item where user_id = #{userId} + and cart_item_id in + + #{shopCartItemId} + + + + + delete + from shop_cart_item + where user_id = #{userId} + + + + + + + + + update shop_cart_item set is_checked = #{item.isChecked} where cart_item_id = #{item.shopCartItemId} and user_id = #{userId} + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SkuMapper.xml b/mall4cloud-product/src/main/resources/mapper/SkuMapper.xml new file mode 100644 index 00000000..1216902a --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SkuMapper.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sku.`sku_id`,sku.`create_time`,sku.`update_time`,sku.sku_name,sku.`spu_id`,sku.`attrs`,sku.`img_url`,sku.`price_fee`,sku.`market_price_fee`,sku.`status` + ,sku.weight,sku.volume,sku.party_code,sku.model_id + + + + + + + + insert into sku (`spu_id`,`attrs`,`img_url`,`price_fee`,`market_price_fee`,`status`) + values (#{sku.spuId},#{sku.attrs},#{sku.imgUrl},#{sku.priceFee},#{sku.marketPriceFee},#{sku.status}); + + + + insert into sku (`spu_id`,`sku_name`,`attrs`,`img_url`,`price_fee`,`market_price_fee`,`party_code`,`model_id`,`status`) values + + (#{sku.spuId},#{sku.skuName},#{sku.attrs},#{sku.imgUrl},#{sku.priceFee},#{sku.marketPriceFee},#{sku.partyCode},#{sku.modelId},#{sku.status}) + + + + + update sku + + + `spu_id` = #{sku.spuId}, + + + `attrs` = #{sku.attrs}, + + + `img_url` = #{sku.imgUrl}, + + + `price_fee` = #{sku.priceFee}, + + + `market_price_fee` = #{sku.marketPriceFee}, + + + `status` = #{sku.status}, + + + where sku_id = #{sku.skuId} + + + delete from sku where sku_id = #{skuId} + + + + update sku set status = -1 where spu_id = #{spuId} + + + + + update sku + + + `attrs` = #{sku.attrs}, + + + `img_url` = #{sku.imgUrl}, + + + `price_fee` = #{sku.priceFee}, + + + `market_price_fee` = #{sku.marketPriceFee}, + + + `party_code` = #{sku.partyCode}, + + + `model_id` = #{sku.modelId}, + + + `status` = #{sku.status}, + + + where sku_id = #{sku.skuId} + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SkuStockLockMapper.xml b/mall4cloud-product/src/main/resources/mapper/SkuStockLockMapper.xml new file mode 100644 index 00000000..8386ecb2 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SkuStockLockMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + `id`, + `create_time`, + `update_time`, + `spu_id`, + `sku_id`, + `order_id`, + `count`, + `status` + + + + + insert into sku_stock_lock (`spu_id`, `sku_id`, `order_id`, `count`, `status`) + values (#{skuStockLock.spuId}, #{skuStockLock.skuId}, #{skuStockLock.orderId}, #{skuStockLock.count}, + #{skuStockLock.status}); + + + update sku_stock_lock + + + `spu_id` = #{skuStockLock.spuId}, + + + `sku_id` = #{skuStockLock.skuId}, + + + `order_id` = #{skuStockLock.orderId}, + + + `count` = #{skuStockLock.count}, + + + `status` = #{skuStockLock.status}, + + + where id = #{skuStockLock.id} + + + delete + from sku_stock_lock + where id = #{id} + + + insert into sku_stock_lock (`spu_id`, `sku_id`, `order_id`, `count`, `status`) + values + + (#{skuStockLock.spuId}, #{skuStockLock.skuId}, #{skuStockLock.orderId}, #{skuStockLock.count}, + #{skuStockLock.status}) + + + + + update sku_stock_lock + set `status` = -1 where id in + + #{id} + + + + + update sku_stock_lock + set `status` = 1 where order_id in + + #{orderId} + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SkuStockMapper.xml b/mall4cloud-product/src/main/resources/mapper/SkuStockMapper.xml new file mode 100644 index 00000000..96272744 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SkuStockMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + `stock_id`, + `create_time`, + `update_time`, + `sku_id`, + `actual_stock`, + `lock_stock`, + `stock` + + + + + + insert into sku_stock (`sku_id`, `actual_stock`, `lock_stock`, `stock`) + values (#{skuStock.skuId}, #{skuStock.actualStock}, #{skuStock.lockStock}, #{skuStock.stock}); + + + + insert into sku_stock (`sku_id`, `actual_stock`, `lock_stock`, `stock`) values + + (#{skuStock.skuId}, #{skuStock.actualStock}, #{skuStock.lockStock}, #{skuStock.stock}) + + + + + update sku_stock + + + `sku_id` = #{skuStock.skuId}, + + + `actual_stock` = #{skuStock.actualStock}, + + + `lock_stock` = #{skuStock.lockStock}, + + + "stock" = #{skuStock.stock}, + + + where stock_id = #{skuStock.stockId} + + + + + update sku_stock + set stock = #{skuStock.stock} + stock, + actual_stock = #{skuStock.stock} + actual_stock + where sku_id = #{skuStock.skuId} + + + + + delete + from sku_stock + where stock_id = #{stockId} + + + + delete + from sku_stock where sku_id in ( + + #{skuId} + + ) + + + + update sku_stock + set `stock` = `stock` - #{count}, + `lock_stock` = `lock_stock` + #{count} + where sku_id = #{skuId} + and #{count} <= `stock` + + + + update sku_stock + set `stock` = `stock` + #{skuWithStock.count}, + `lock_stock` = `lock_stock` - #{skuWithStock.count} + where sku_id = #{skuWithStock.skuId} + + + + + + update sku_stock + set `actual_stock` = `actual_stock` + #{skuWithStock.count}, + `lock_stock` = `lock_stock` - #{skuWithStock.count} + where sku_id = #{skuWithStock.skuId} + + + + + + + update sku_stock + set `actual_stock` = `actual_stock` + #{skuWithStock.count}, + `lock_stock` = `lock_stock` - #{skuWithStock.count}, + stock = stock - #{skuWithStock.count} + where sku_id = #{skuWithStock.skuId} + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SpuAttrValueMapper.xml b/mall4cloud-product/src/main/resources/mapper/SpuAttrValueMapper.xml new file mode 100644 index 00000000..7ea6dc93 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SpuAttrValueMapper.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + `spu_attr_value_id`,`spu_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name` + + + insert into spu_attr_value (`spu_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name`) + values (#{spuAttrValue.spuId},#{spuAttrValue.attrId},#{spuAttrValue.attrName},#{spuAttrValue.attrValueId},#{spuAttrValue.attrValueName}); + + + + insert into spu_attr_value (`spu_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name`)values + + (#{spuAttrValue.spuId},#{spuAttrValue.attrId},#{spuAttrValue.attrName},#{spuAttrValue.attrValueId},#{spuAttrValue.attrValueName}) + + + + + delete from spu_attr_value where spu_id = #{spuId} + + + + update spu_attr_value + + + `spu_id` = #{spuAttrValue.spuId}, + + + `attr_id` = #{spuAttrValue.attrId}, + + + `attr_name` = #{spuAttrValue.attrName}, + + + `attr_value_id` = #{spuAttrValue.attrValueId}, + + + `attr_value_name` = #{spuAttrValue.attrValueName}, + + + where spu_attr_value_id = #{spuAttrValue.spuAttrValueId} + + + + DELETE FROM spu_attr_value WHERE attr_id = #{attrId} + + AND attr_value_id IN + + #{attrValueId} + + + + + + + + + update spu_attr_value + + + `attr_value_id` = #{spuAttrValue.attrValueId} + + + ,`attr_value_name` = #{spuAttrValue.attrValueName} + + + where spu_attr_value_id = #{spuAttrValue.spuAttrValueId}; + + + + + delete from spu_attr_value where spu_attr_value_id in + + #{spuAttrValueId} + + + + + + update spu_attr_value + + + `attr_name` = #{spuAttrValue.attrName} + + + ,`attr_value_name` = #{spuAttrValue.attrValueName} + + + where `attr_value_id` = #{spuAttrValue.attrValueId} and attr_id = #{spuAttrValue.attrId}; + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SpuDetailMapper.xml b/mall4cloud-product/src/main/resources/mapper/SpuDetailMapper.xml new file mode 100644 index 00000000..08b19e43 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SpuDetailMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + `spu_id`,`create_time`,`update_time`,`detail` + + + + insert into spu_detail (`spu_id`,`detail`) + values (#{spuDetail.spuId},#{spuDetail.detail}); + + + update spu_detail + + + `detail` = #{spuDetail.detail}, + + + where spu_id = #{spuDetail.spuId} + + + delete from spu_detail where spu_id = #{spuId} + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SpuExtensionMapper.xml b/mall4cloud-product/src/main/resources/mapper/SpuExtensionMapper.xml new file mode 100644 index 00000000..15ef5857 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SpuExtensionMapper.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + `spu_extend_id`, + `create_time`, + `update_time`, + `spu_id`, + `sale_num`, + `actual_stock`, + `lock_stock`, + `stock` + + + + + insert into spu_extension (`spu_id`, `sale_num`, `actual_stock`, `lock_stock`, `stock`) + values (#{spuExtension.spuId}, 0, #{spuExtension.actualStock}, 0, #{spuExtension.stock}); + + + + delete + from spu_extension + where spu_id = #{spuId} + + + + update spu_extension + set `stock` = `stock` + #{count}, + actual_stock = actual_stock + #{count} + where spu_id = #{spuId} + + + + update spu_extension + set `stock` = `stock` - #{count}, + `lock_stock` = `lock_stock` + #{count} + where spu_id = #{spuId} + and #{count} <= `stock` + + + + + update spu_extension + set `stock` = `stock` + #{skuWithStock.count}, + `lock_stock` = `lock_stock` - #{skuWithStock.count} + where spu_id = #{skuWithStock.spuId} + + + + + + update spu_extension + set `lock_stock` = `lock_stock` - #{skuWithStock.count}, + `actual_stock` = `actual_stock` - #{skuWithStock.count}, + `sale_num` = `sale_num` + #{skuWithStock.count} + where spu_id = #{skuWithStock.spuId} + + + + + + update spu_extension + set `lock_stock` = `lock_stock` - #{skuWithStock.count}, + `actual_stock` = `actual_stock` - #{skuWithStock.count}, + `stock` = `stock` - #{skuWithStock.count}, + `sale_num` = `sale_num` + #{skuWithStock.count} + where spu_id = #{skuWithStock.spuId} + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SpuMapper.xml b/mall4cloud-product/src/main/resources/mapper/SpuMapper.xml new file mode 100644 index 00000000..9b31cea0 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SpuMapper.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s.`spu_id`,s.`shop_id`,s.`create_time`,s.`update_time`,s.`brand_id`,s.`shop_category_id`,s.`category_id`,s.`name`,s.`selling_point`,s.`img_urls`,s.`main_img_url`,s.`price_fee` + ,s.`market_price_fee`,s.`status`,s.`has_sku_img`,s.`seq` + + + + + + + insert into spu (`brand_id`,`shop_id`,`category_id`,`shop_category_id`,`name`,`selling_point`,`img_urls`,`main_img_url`,`price_fee`,`market_price_fee`,`status`,`seq`) + values (#{spu.brandId},#{spu.shopId},#{spu.categoryId},#{spu.shopCategoryId},#{spu.name},#{spu.sellingPoint},#{spu.imgUrls},#{spu.mainImgUrl},#{spu.priceFee},#{spu.marketPriceFee},#{spu.status},#{spu.seq}); + + + update spu set status = -1 where spu_id = #{spuId} + + + update spu + + + `brand_id` = #{spu.brandId}, + + + `category_id` = #{spu.categoryId}, + + + `shop_category_id` = #{spu.shopCategoryId}, + + + `name` = #{spu.name}, + + + `selling_point` = #{spu.sellingPoint}, + + + `img_urls` = #{spu.imgUrls}, + + + `main_img_url` = #{spu.mainImgUrl}, + + + `price_fee` = #{spu.priceFee}, + + + `market_price_fee` = #{spu.marketPriceFee}, + + + `status` = #{spu.status}, + + + `has_sku_img` = #{spu.hasSkuImg}, + + + `seq` = #{spu.seq}, + + + where spu_id = #{spu.spuId} + + + delete from spu where spu_id = #{spuId} + + + + + + + + update spu set `status` = #{status} where spu_id = #{spuId} + + + update spu set `update_time` = NOW() + + + category_id in + + #{categoryId} + + + + and spu_id in + + #{spuId} + + + + + + update spu set status = #{status} + where status ]]> -1 and spu_id in + + #{spuId} + + + + + + + + diff --git a/mall4cloud-product/src/main/resources/mapper/SpuSkuAttrValueMapper.xml b/mall4cloud-product/src/main/resources/mapper/SpuSkuAttrValueMapper.xml new file mode 100644 index 00000000..67bb8c91 --- /dev/null +++ b/mall4cloud-product/src/main/resources/mapper/SpuSkuAttrValueMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + `spu_sku_attr_id`,`create_time`,`update_time`,`spu_id`,`sku_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name`,`status` + + + insert into spu_sku_attr_value (`spu_id`,`sku_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name`,`status`) + values (#{spuSkuAttrValue.spuId},#{spuSkuAttrValue.skuId},#{spuSkuAttrValue.attrId},#{spuSkuAttrValue.attrName},#{spuSkuAttrValue.attrValueId},#{spuSkuAttrValue.attrValueName},#{spuSkuAttrValue.status}); + + + + insert into spu_sku_attr_value (`spu_id`,`sku_id`,`attr_id`,`attr_name`,`attr_value_id`,`attr_value_name`,`status`) values + + (#{spuSkuAttrValue.spuId},#{spuSkuAttrValue.skuId},#{spuSkuAttrValue.attrId},#{spuSkuAttrValue.attrName}, + #{spuSkuAttrValue.attrValueId},#{spuSkuAttrValue.attrValueName},#{spuSkuAttrValue.status}) + + + + + update spu_sku_attr_value set status = -1 where spu_id = #{spuId} + + + + + update spu_sku_attr_value + + + `attr_name` = #{spuSkuAttrValue.attrName}, + + + `attr_value_name` = #{spuSkuAttrValue.attrValueName}, + + + where spu_sku_attr_id = #{spuSkuAttrValue.spuSkuAttrId}; + + + + + update spu_sku_attr_value + + + `spu_id` = #{spuSkuAttrValue.spuId}, + + + `sku_id` = #{spuSkuAttrValue.skuId}, + + + `attr_id` = #{spuSkuAttrValue.attrId}, + + + `attr_name` = #{spuSkuAttrValue.attrName}, + + + `attr_value_id` = #{spuSkuAttrValue.attrValueId}, + + + `attr_value_name` = #{spuSkuAttrValue.attrValueName}, + + + `status` = #{spuSkuAttrValue.status}, + + + where spu_sku_attr_id = #{spuSkuAttrValue.spuSkuAttrId} + + + delete from spu_sku_attr_value where spu_sku_attr_id = #{spuSkuAttrId} + + + + update spu_sku_attr_value set `status` = #{status} where sku_id in + + #{skuId} + + + diff --git a/mall4cloud-rbac/pom.xml b/mall4cloud-rbac/pom.xml new file mode 100644 index 00000000..a152cdc0 --- /dev/null +++ b/mall4cloud-rbac/pom.xml @@ -0,0 +1,49 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-rbac + jar + + mall4cloud 用户角色权限模块 + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/RbacApplication.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/RbacApplication.java new file mode 100644 index 00000000..57b084fd --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/RbacApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.rbac; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/6/24 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class RbacApplication { + + public static void main(String[] args) { + SpringApplication.run(RbacApplication.class, args); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/config/SwaggerConfiguration.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/config/SwaggerConfiguration.java new file mode 100644 index 00000000..a0e73e60 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.rbac.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.rbac.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuController.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuController.java new file mode 100644 index 00000000..c11fdd70 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuController.java @@ -0,0 +1,140 @@ +package com.mall4j.cloud.rbac.controller; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.rbac.dto.MenuDTO; +import com.mall4j.cloud.rbac.model.Menu; +import com.mall4j.cloud.rbac.service.MenuService; +import com.mall4j.cloud.rbac.vo.MenuSimpleVO; +import com.mall4j.cloud.rbac.vo.MenuVO; +import com.mall4j.cloud.rbac.vo.RouteMetaVO; +import com.mall4j.cloud.rbac.vo.RouteVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/08/06 + */ +@RequestMapping(value = "/menu") +@RestController +@Api(tags = "菜单接口") +public class MenuController { + + @Autowired + private MenuService menuService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping(value = "/route") + @ApiOperation(value = "路由菜单", notes = "获取当前登陆用户可用的路由菜单列表") + public ServerResponseEntity> route(Integer sysType) { + sysType = Objects.isNull(sysType) ? AuthUserContext.get().getSysType(): sysType; + List menus = menuService.listBySysType(sysType); + + List routes = new ArrayList<>(menus.size()); + + for (Menu menu : menus) { + RouteVO route = new RouteVO(); + route.setAlwaysShow(BooleanUtil.isTrue(menu.getAlwaysShow())); + route.setComponent(menu.getComponent()); + route.setHidden(BooleanUtil.isTrue(menu.getHidden())); + route.setName(menu.getName()); + route.setPath(menu.getPath()); + route.setRedirect(menu.getRedirect()); + route.setId(menu.getMenuId()); + route.setParentId(menu.getParentId()); + route.setSeq(menu.getSeq()); + + RouteMetaVO meta = new RouteMetaVO(); + meta.setActiveMenu(menu.getActiveMenu()); + meta.setAffix(BooleanUtil.isTrue(menu.getAffix())); + meta.setBreadcrumb(BooleanUtil.isTrue(menu.getBreadcrumb())); + meta.setIcon(menu.getIcon()); + meta.setNoCache(BooleanUtil.isTrue(menu.getNoCache())); + meta.setTitle(menu.getTitle()); + // 对于前端来说角色就是权限 + meta.setRoles(Collections.singletonList(menu.getPermission())); + + route.setMeta(meta); + routes.add(route); + } + return ServerResponseEntity.success(routes); + } + + @GetMapping + @ApiOperation(value = "获取菜单管理", notes = "根据menuId获取菜单管理") + public ServerResponseEntity getByMenuId(@RequestParam Long menuId) { + return ServerResponseEntity.success(menuService.getByMenuId(menuId)); + } + + @PostMapping + @ApiOperation(value = "保存菜单管理", notes = "保存菜单管理") + public ServerResponseEntity save(@Valid @RequestBody MenuDTO menuDTO) { + Menu menu = checkAndGenerate(menuDTO); + menu.setMenuId(null); + menuService.save(menu); + return ServerResponseEntity.success(); + } + + private Menu checkAndGenerate(@RequestBody @Valid MenuDTO menuDTO) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + if(!Objects.equals(userInfoInTokenBO.getTenantId(),0L)){ + throw new mall4cloudException("无权限操作!"); + } + Menu menu = mapperFacade.map(menuDTO, Menu.class); + menu.setBizType(menuDTO.getSysType()); + if(Objects.isNull(menuDTO.getSysType())){ + menu.setBizType(AuthUserContext.get().getSysType()); + } + return menu; + } + + @PutMapping + @ApiOperation(value = "更新菜单管理", notes = "更新菜单管理") + public ServerResponseEntity update(@Valid @RequestBody MenuDTO menuDTO) { + Menu menu = checkAndGenerate(menuDTO); + menuService.update(menu); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除菜单管理", notes = "根据菜单管理id删除菜单管理") + public ServerResponseEntity delete(@RequestParam("menuId") Long menuId,@RequestParam("sysType") Integer sysType) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + if(!Objects.equals(userInfoInTokenBO.getTenantId(),0L)){ + throw new mall4cloudException("无权限操作!"); + } + sysType = Objects.isNull(sysType) ? userInfoInTokenBO.getSysType():sysType; + menuService.deleteById(menuId,sysType); + return ServerResponseEntity.success(); + } + + @GetMapping(value = "/list_with_permissions") + @ApiOperation(value = "菜单列表和按钮列表", notes = "根据系统类型获取该系统的菜单列表 + 菜单下的权限列表") + public ServerResponseEntity> listWithPermissions() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + List menuList = menuService.listWithPermissions(userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(menuList); + } + + @GetMapping(value = "/list_menu_ids") + @ApiOperation(value = "获取当前用户可见的菜单ids", notes = "获取当前用户可见的菜单id") + public ServerResponseEntity> listMenuIds() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + List menuList = menuService.listMenuIds(userInfoInTokenBO.getUserId()); + return ServerResponseEntity.success(menuList); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuPermissionController.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuPermissionController.java new file mode 100644 index 00000000..7f9989a7 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/MenuPermissionController.java @@ -0,0 +1,92 @@ +package com.mall4j.cloud.rbac.controller; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.rbac.dto.MenuPermissionDTO; +import com.mall4j.cloud.rbac.model.MenuPermission; +import com.mall4j.cloud.rbac.service.MenuPermissionService; +import com.mall4j.cloud.rbac.vo.MenuPermissionVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/09/02 + */ +@RequestMapping(value = "/menu_permission") +@RestController +@Api(tags = "权限接口") +public class MenuPermissionController { + + @Autowired + private MenuPermissionService menuPermissionService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/list_by_menu") + @ApiOperation(value = "获取菜单资源列表", notes = "分页获取菜单资源列表") + public ServerResponseEntity> listByMenuId(Long menuId) { + List menuPermissionVOList = menuPermissionService.listByMenuId(menuId); + return ServerResponseEntity.success(menuPermissionVOList); + } + + @GetMapping + @ApiOperation(value = "获取菜单资源", notes = "根据menuPermissionId获取菜单资源") + public ServerResponseEntity getByMenuPermissionId(@RequestParam Long menuPermissionId) { + return ServerResponseEntity.success(menuPermissionService.getByMenuPermissionId(menuPermissionId)); + } + + @PostMapping + @ApiOperation(value = "保存菜单资源", notes = "保存菜单资源") + public ServerResponseEntity save(@Valid @RequestBody MenuPermissionDTO menuPermissionDTO) { + MenuPermission menuPermission = mapperFacade.map(menuPermissionDTO, MenuPermission.class); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + menuPermission.setMenuPermissionId(null); + menuPermission.setBizType(userInfoInTokenBO.getSysType()); + return menuPermissionService.save(menuPermission); + } + + @PutMapping + @ApiOperation(value = "更新菜单资源", notes = "更新菜单资源") + public ServerResponseEntity update(@Valid @RequestBody MenuPermissionDTO menuPermissionDTO) { + MenuPermission menuPermission = mapperFacade.map(menuPermissionDTO, MenuPermission.class); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + menuPermission.setBizType(userInfoInTokenBO.getSysType()); + return menuPermissionService.update(menuPermission); + } + + @DeleteMapping + @ApiOperation(value = "删除菜单资源", notes = "根据菜单资源id删除菜单资源") + public ServerResponseEntity delete(@RequestParam Long menuPermissionId) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + menuPermissionService.deleteById(menuPermissionId,userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(); + } + + @GetMapping(value = "/list") + @ApiOperation(value = "获取当前用户拥有的权限", notes = "当前用户所拥有的所有权限,精确到按钮,实际上element admin里面的roles就可以理解成权限") + public ServerResponseEntity> permissions() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + return ServerResponseEntity.success(menuPermissionService.listUserPermissions(userInfoInTokenBO.getUserId(), + userInfoInTokenBO.getSysType(), BooleanUtil.isTrue(userInfoInTokenBO.getIsAdmin()))); + } + + @GetMapping(value = "/page") + @ApiOperation(value = "获取当前用户拥有的权限", notes = "当前用户所拥有的所有权限,精确到按钮,实际上element admin里面的roles就可以理解成权限") + public ServerResponseEntity> pagePermissions(PageDTO pageDTO) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + PageVO permissionPage = menuPermissionService.page(pageDTO, userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(permissionPage); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/RoleController.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/RoleController.java new file mode 100644 index 00000000..379908fc --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/controller/RoleController.java @@ -0,0 +1,105 @@ +package com.mall4j.cloud.rbac.controller; + +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.rbac.model.Role; +import com.mall4j.cloud.rbac.service.RoleService; +import com.mall4j.cloud.rbac.vo.RoleVO; +import com.mall4j.cloud.rbac.dto.RoleDTO; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * 角色 + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +@RestController +@RequestMapping("/role") +@Api(tags = "角色") +public class RoleController { + + @Autowired + private RoleService roleService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "分页获取角色列表", notes = "分页获取角色列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + PageVO rolePage = roleService.page(pageDTO, userInfoInTokenBO.getSysType(), userInfoInTokenBO.getTenantId()); + return ServerResponseEntity.success(rolePage); + } + + @GetMapping("/list") + @ApiOperation(value = "获取角色列表", notes = "分页获取角色列表") + public ServerResponseEntity> list() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + return ServerResponseEntity.success(roleService.list(userInfoInTokenBO.getSysType(), userInfoInTokenBO.getTenantId())); + } + + @GetMapping + @ApiOperation(value = "获取角色", notes = "根据roleId获取角色") + public ServerResponseEntity getByRoleId(@RequestParam Long roleId) { + return ServerResponseEntity.success(roleService.getByRoleId(roleId)); + } + + @PostMapping + @ApiOperation(value = "保存角色", notes = "保存角色") + public ServerResponseEntity save(@Valid @RequestBody RoleDTO roleDTO) { + Role role = mapperFacade.map(roleDTO, Role.class); + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + role.setBizType(userInfoInTokenBO.getSysType()); + role.setRoleId(null); + role.setCreateUserId(userInfoInTokenBO.getUserId()); + role.setTenantId(userInfoInTokenBO.getTenantId()); + roleService.save(role, roleDTO.getMenuIds(), roleDTO.getMenuPermissionIds()); + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新角色", notes = "更新角色") + public ServerResponseEntity update(@Valid @RequestBody RoleDTO roleDTO) { + + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + + + RoleVO dbRole = roleService.getByRoleId(roleDTO.getRoleId()); + + if (!Objects.equals(dbRole.getBizType(), userInfoInTokenBO.getSysType()) || !Objects.equals(dbRole.getTenantId(), userInfoInTokenBO.getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + Role role = mapperFacade.map(roleDTO, Role.class); + role.setBizType(userInfoInTokenBO.getSysType()); + + roleService.update(role, roleDTO.getMenuIds(), roleDTO.getMenuPermissionIds()); + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除角色", notes = "根据角色id删除角色") + public ServerResponseEntity delete(@RequestParam Long roleId) { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + + RoleVO dbRole = roleService.getByRoleId(roleId); + + if (!Objects.equals(dbRole.getBizType(), userInfoInTokenBO.getSysType()) || !Objects.equals(dbRole.getTenantId(), userInfoInTokenBO.getTenantId())) { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + roleService.deleteById(roleId,userInfoInTokenBO.getSysType()); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuDTO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuDTO.java new file mode 100644 index 00000000..413004c5 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuDTO.java @@ -0,0 +1,169 @@ +package com.mall4j.cloud.rbac.dto; + +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 菜单管理DTO + * + * @author FrozenWatermelon + * @date 2020-09-15 16:35:01 + */ +public class MenuDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单id") + private Long menuId; + + @NotNull(message = "parentId NotNull") + @ApiModelProperty("父菜单ID,一级菜单为0") + private Long parentId; + + @ApiModelProperty("权限,需要有哪个权限才能访问该菜单") + private String permission; + + @ApiModelProperty("路径 就像uri") + private String path; + + @NotBlank(message = "component NotBlank") + @ApiModelProperty("组件如:1.'Layout' 为布局,不会跳页面 2.'components-demo/tinymce' 跳转到该页面") + private String component; + + @ApiModelProperty("当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1") + private Integer hidden; + + @NotBlank(message = "name NotBlank") + @ApiModelProperty("设定路由的名字,一定要填写不然使用时会出现各种问题") + private String name; + + @NotBlank(message = "title NotBlank") + @ApiModelProperty("设置该路由在侧边栏和面包屑中展示的名字") + private String title; + + @ApiModelProperty("系统类型") + private Integer sysType; + + @ApiModelProperty("设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon") + private String icon; + + @ApiModelProperty("当路由设置了该属性,则会高亮相对应的侧边栏。") + private String activeMenu; + + @ApiModelProperty("排序,越小越靠前") + private Integer seq; + + public Integer getSysType() { + return sysType; + } + + public void setSysType(Integer sysType) { + this.sysType = sysType; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public Integer getHidden() { + return hidden; + } + + public void setHidden(Integer hidden) { + this.hidden = hidden; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getActiveMenu() { + return activeMenu; + } + + public void setActiveMenu(String activeMenu) { + this.activeMenu = activeMenu; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "MenuDTO{" + + "menuId=" + menuId + + ", parentId=" + parentId + + ", permission='" + permission + '\'' + + ", path='" + path + '\'' + + ", component='" + component + '\'' + + ", hidden=" + hidden + + ", name='" + name + '\'' + + ", title='" + title + '\'' + + ", sysType=" + sysType + + ", icon='" + icon + '\'' + + ", activeMenu='" + activeMenu + '\'' + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuPermissionDTO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuPermissionDTO.java new file mode 100644 index 00000000..0dd25879 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuPermissionDTO.java @@ -0,0 +1,98 @@ +package com.mall4j.cloud.rbac.dto; + +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 菜单资源DTO + * + * @author FrozenWatermelon + * @date 2020-09-15 16:35:01 + */ +public class MenuPermissionDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单资源用户id") + private Long menuPermissionId; + + @NotNull(message = "menuId NotNull") + @ApiModelProperty("资源关联菜单") + private Long menuId; + + @NotBlank(message = "permission NotBlank") + @ApiModelProperty("权限对应的编码") + private String permission; + + @ApiModelProperty("资源名称") + private String name; + + @ApiModelProperty("资源对应服务器路径") + @NotBlank(message = "uri NotBlank") + private String uri; + + @NotNull(message = "method NotNull") + @ApiModelProperty("请求方法 1.GET 2.POST 3.PUT 4.DELETE") + private Integer method; + + public Long getMenuPermissionId() { + return menuPermissionId; + } + + public void setMenuPermissionId(Long menuPermissionId) { + this.menuPermissionId = menuPermissionId; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public Integer getMethod() { + return method; + } + + public void setMethod(Integer method) { + this.method = method; + } + + @Override + public String toString() { + return "MenuPermissionVO{" + + "menuPermissionId=" + menuPermissionId + + ",menuId=" + menuId + + ",permission=" + permission + + ",name=" + name + + ",uri=" + uri + + ",method=" + method + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuWithPermissionIdDTO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuWithPermissionIdDTO.java new file mode 100644 index 00000000..86fd4e89 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/MenuWithPermissionIdDTO.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.rbac.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * 菜单id和权限id列表 + * @author FrozenWatermelon + * @date 2020/9/18 + */ +public class MenuWithPermissionIdDTO { + + @ApiModelProperty("菜单id") + private Long menuId; + + @ApiModelProperty("菜单下的权限id列表") + private List permissionIds; + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public List getPermissionIds() { + return permissionIds; + } + + public void setPermissionIds(List permissionIds) { + this.permissionIds = permissionIds; + } + + @Override + public String toString() { + return "MenuWithPermissionIdDTO{" + + "menuId=" + menuId + + ", permissionIds=" + permissionIds + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/RoleDTO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/RoleDTO.java new file mode 100644 index 00000000..1debf2ac --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/dto/RoleDTO.java @@ -0,0 +1,80 @@ +package com.mall4j.cloud.rbac.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * 角色DTO + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +public class RoleDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("角色id") + private Long roleId; + + @ApiModelProperty("角色名称") + private String roleName; + + @ApiModelProperty("备注") + private String remark; + + @ApiModelProperty("菜单id列表") + private List menuIds; + + @ApiModelProperty("菜单资源id列表") + private List menuPermissionIds; + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public List getMenuIds() { + return menuIds; + } + + public void setMenuIds(List menuIds) { + this.menuIds = menuIds; + } + + public List getMenuPermissionIds() { + return menuPermissionIds; + } + + public void setMenuPermissionIds(List menuPermissionIds) { + this.menuPermissionIds = menuPermissionIds; + } + + @Override + public String toString() { + return "RoleDTO{" + + "roleId=" + roleId + + ", roleName='" + roleName + '\'' + + ", remark='" + remark + '\'' + + ", menuIds=" + menuIds + + ", menuPermissionIds=" + menuPermissionIds + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/feign/PermissionFeignController.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/feign/PermissionFeignController.java new file mode 100644 index 00000000..75ef28a2 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/feign/PermissionFeignController.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.rbac.feign; + +import com.mall4j.cloud.api.rbac.bo.UriPermissionBO; +import com.mall4j.cloud.api.rbac.dto.ClearUserPermissionsCacheDTO; +import com.mall4j.cloud.api.rbac.feign.PermissionFeignClient; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.rbac.service.MenuPermissionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/7/15 + */ +@RestController +public class PermissionFeignController implements PermissionFeignClient { + + private static final Logger logger = LoggerFactory.getLogger(PermissionFeignController.class); + + @Autowired + private MenuPermissionService menuPermissionService; + + @Override + public ServerResponseEntity checkPermission(@RequestParam("userId") Long userId, @RequestParam("sysType") Integer sysType, @RequestParam("uri") String uri, @RequestParam("isAdmin") Integer isAdmin, @RequestParam("method") Integer method) { + // 目前该用户拥有的权限 + List userPermissions = menuPermissionService.listUserPermissions(userId, + sysType, BooleanUtil.isTrue(isAdmin)); + + // uri,方法对应的权限 map + List uriPermissions = menuPermissionService + .listUriPermissionInfo(sysType); + + AntPathMatcher pathMatcher = new AntPathMatcher(); + + // 看看该uri对应需要什么权限 + for (UriPermissionBO uriPermission : uriPermissions) { + // uri + 请求方式匹配 + if (pathMatcher.match(uriPermission.getUri(), uri) && Objects.equals(uriPermission.getMethod(), method)) { + // uri 用户有这个权限 + if (userPermissions.contains(uriPermission.getPermission())) { + return ServerResponseEntity.success(Boolean.TRUE); + } + else { + return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED); + } + } + } + + // 如果uri 没有匹配到,则说明uri不需要权限,直接校验成功 + return ServerResponseEntity.success(Boolean.TRUE); + } + + @Override + public ServerResponseEntity clearUserPermissionsCache(ClearUserPermissionsCacheDTO clearUserPermissionsCacheDTO) { + menuPermissionService.clearUserPermissionsCache(clearUserPermissionsCacheDTO.getUserId(), clearUserPermissionsCacheDTO.getSysType()); + return ServerResponseEntity.success(); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuMapper.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuMapper.java new file mode 100644 index 00000000..1e2ac644 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuMapper.java @@ -0,0 +1,76 @@ +package com.mall4j.cloud.rbac.mapper; + +import com.mall4j.cloud.rbac.model.Menu; +import com.mall4j.cloud.rbac.vo.MenuSimpleVO; +import com.mall4j.cloud.rbac.vo.MenuVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author FrozenWatermelon + */ +public interface MenuMapper { + + /** + * 根据菜单管理id获取菜单管理 + * + * @param menuId 菜单管理id + * @return 菜单管理 + */ + MenuVO getByMenuId(@Param("menuId") Long menuId); + + /** + * 保存菜单管理 + * + * @param menu 菜单管理 + */ + void save(@Param("menu") Menu menu); + + /** + * 更新菜单管理 + * + * @param menu 菜单管理 + */ + void update(@Param("menu") Menu menu); + + /** + * 根据菜单管理id删除菜单管理 + * + * @param menuId + * @param sysType + */ + void deleteById(@Param("menuId") Long menuId, @Param("sysType") Integer sysType); + + /** + * 根据系统类型获取该系统的菜单列表 + * + * @param sysType 系统类型 + * @return 菜单列表 + */ + List listBySysType(@Param("sysType") Integer sysType); + + /** + * 根据系统类型获取该系统的菜单列表,只有id和名字 + * + * @param sysType sysType 系统类型 + * @return 简易菜单信息列表 + */ + List listSimpleMenuBySytType(Integer sysType); + + /** + * 根据系统类型获取该系统的菜单列表 + 菜单下的权限列表 + * + * @param sysType 系统类型 + * @return 菜单列表 + 菜单下的权限列表 + */ + List listWithPermissions(Integer sysType); + + /** + * 获取当前用户可见的菜单ids + * + * @param userId 用户id + * @return 菜单列表 + */ + List listMenuIds(@Param("userId") Long userId); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuPermissionMapper.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuPermissionMapper.java new file mode 100644 index 00000000..f8a70b49 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/MenuPermissionMapper.java @@ -0,0 +1,95 @@ +package com.mall4j.cloud.rbac.mapper; + +import com.mall4j.cloud.api.rbac.bo.UriPermissionBO; +import com.mall4j.cloud.rbac.model.MenuPermission; +import com.mall4j.cloud.rbac.vo.MenuPermissionVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/09/03 + */ +public interface MenuPermissionMapper { + + /** + * 获取菜单资源列表 + * + * @param sysType 系统类型 + * @return 菜单资源列表 + */ + List list(@Param("sysType") Integer sysType); + + /** + * 根据菜单资源id获取菜单资源 + * + * @param menuPermissionId 菜单资源id + * @return 菜单资源 + */ + MenuPermissionVO getByMenuPermissionId(@Param("menuPermissionId") Long menuPermissionId); + + /** + * 保存菜单资源 + * + * @param menuPermission 菜单资源 + */ + void save(@Param("menuPermission") MenuPermission menuPermission); + + /** + * 更新菜单资源 + * + * @param menuPermission 菜单资源 + */ + void update(@Param("menuPermission") MenuPermission menuPermission); + + /** + * 根据菜单资源id删除菜单资源 + * + * @param menuPermissionId + * @param sysType + */ + void deleteById(@Param("menuPermissionId") Long menuPermissionId, @Param("sysType") Integer sysType); + + /** + * 获取某个类型用户的所有权限列表 + * + * @param sysType 系统类型 + * @return 权限列表 + */ + List listAllPermissionBySysType(@Param("sysType") Integer sysType); + + /** + * 获取某个用户的权限列表 + * + * @param userId 用户id + * @param sysType 系统类型 + * @return 权限列表 + */ + List listPermissionByUserIdAndSysType(@Param("userId") Long userId, @Param("sysType") Integer sysType); + + /** + * 根据系统类型,获取该类型用户拥有的所有权限数据 + * + * @param sysType 系统类型 + * @return uri权限列表 + */ + List listUriPermissionInfo(@Param("sysType") Integer sysType); + + /** + * 根据menuId获取菜单资源列表 + * + * @param menuId 菜单id + * @return 菜单资源列表数据 + */ + List listByMenuId(@Param("menuId") Long menuId); + + /** + * 通过权限对应的编码获取权限信息 + * + * @param permission 权限对应的编码 + * @param sysType 系统类型 + * @return + */ + MenuPermission getByPermission(@Param("permission") String permission, @Param("sysType") Integer sysType); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMapper.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMapper.java new file mode 100644 index 00000000..a8e00699 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMapper.java @@ -0,0 +1,63 @@ +package com.mall4j.cloud.rbac.mapper; + +import com.mall4j.cloud.rbac.model.Role; +import com.mall4j.cloud.rbac.vo.RoleVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 角色 + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +public interface RoleMapper { + + /** + * 获取角色列表 + * + * @param sysType + * @param tenantId + * @return 角色列表 + */ + List list(@Param("sysType") Integer sysType, @Param("tenantId") Long tenantId); + + /** + * 根据角色id获取角色 + * + * @param roleId 角色id + * @return 角色 + */ + RoleVO getByRoleId(@Param("roleId") Long roleId); + + /** + * 保存角色 + * + * @param role 角色 + */ + void save(@Param("role") Role role); + + /** + * 更新角色 + * + * @param role 角色 + */ + void update(@Param("role") Role role); + + /** + * 根据角色id删除角色 + * + * @param roleId 角色id + * @param sysType 系统类型 + */ + void deleteById(@Param("roleId") Long roleId, @Param("sysType") Integer sysType); + + /** + * 根据角色id获取该角色所在系统 + * + * @param roleId 角色id + * @return sysType + */ + Integer getBizType(@Param("roleId") Long roleId); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMenuMapper.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMenuMapper.java new file mode 100644 index 00000000..0f011349 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/RoleMenuMapper.java @@ -0,0 +1,32 @@ +package com.mall4j.cloud.rbac.mapper; + +import com.mall4j.cloud.rbac.model.RoleMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/6/24 + */ +public interface RoleMenuMapper { + + /** + * 批量插入角色菜单关联关系 + * @param roleMenus 角色菜单对象列表 + */ + void insertBatch(@Param("roleMenus") List roleMenus); + + /** + * 根据角色id删除角色与菜单的关联关系 + * @param roleId + */ + void deleteByRoleId(@Param("roleId") Long roleId); + + /** + * 根据角色id,获取角色菜单关联关系列表 + * @param roleId 角色id + * @return 角色菜单关联关系列表 + */ + List getByRoleId(@Param("roleId") Long roleId); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/UserRoleMapper.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/UserRoleMapper.java new file mode 100644 index 00000000..c170f098 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/mapper/UserRoleMapper.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.rbac.mapper; + +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/6/24 + */ +public interface UserRoleMapper { + + /** + * 根据用户id删除用户与角色关系 + * @param userId + */ + void deleteByUserId(Long userId); + + /** + * 根据用户id 批量添加用户角色关系 + * @param userId + * @param roleIdList + */ + void insertUserAndUserRole(@Param("userId") Long userId, @Param("roleIdList") List roleIdList); + + /** + * 根据用户id 获取用户角色关系 + * @param userId 用户id + * @return 角色id列表 + */ + List getRoleIds(Long userId); + + /** + * 根据角色id 删除用户角色关系 + * @param roleId 用户id + */ + void deleteByRoleId(Long roleId); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Menu.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Menu.java new file mode 100644 index 00000000..b00797dd --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Menu.java @@ -0,0 +1,260 @@ +package com.mall4j.cloud.rbac.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 菜单管理 + * + * @author FrozenWatermelon + * @date 2020-09-15 16:36:50 + */ +public class Menu extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 菜单id + */ + private Long menuId; + + /** + * 父菜单ID,一级菜单为0 + */ + private Long parentId; + + /** + * 业务类型 0平台菜单 1 店铺菜单 + */ + private Integer bizType; + + /** + * 权限,需要有哪个权限才能访问该菜单 + */ + private String permission; + + /** + * 路径 就像uri + */ + private String path; + + /** + * 组件如:1.'Layout' 为布局,不会跳页面 2.'components-demo/tinymce' 跳转到该页面 + */ + private String component; + + /** + * 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 一直显示根路由 + */ + private Integer alwaysShow; + + /** + * 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + */ + private Integer hidden; + + /** + * 设定路由的名字,一定要填写不然使用时会出现各种问题 + */ + private String name; + + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon + */ + private String icon; + + /** + * 如果设置为true,则不会被 缓存(默认 false) + */ + private Integer noCache; + + /** + * 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) + */ + private Integer breadcrumb; + + /** + * 若果设置为true,它则会固定在tags-view中(默认 false) + */ + private Integer affix; + + /** + * 当路由设置了该属性,则会高亮相对应的侧边栏。 + */ + private String activeMenu; + + /** + * 排序,越小越靠前 + */ + private Integer seq; + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public Integer getBizType() { + return bizType; + } + + public void setBizType(Integer bizType) { + this.bizType = bizType; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getRedirect() { + return redirect; + } + + public void setRedirect(String redirect) { + this.redirect = redirect; + } + + public Integer getAlwaysShow() { + return alwaysShow; + } + + public void setAlwaysShow(Integer alwaysShow) { + this.alwaysShow = alwaysShow; + } + + public Integer getHidden() { + return hidden; + } + + public void setHidden(Integer hidden) { + this.hidden = hidden; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public Integer getNoCache() { + return noCache; + } + + public void setNoCache(Integer noCache) { + this.noCache = noCache; + } + + public Integer getBreadcrumb() { + return breadcrumb; + } + + public void setBreadcrumb(Integer breadcrumb) { + this.breadcrumb = breadcrumb; + } + + public Integer getAffix() { + return affix; + } + + public void setAffix(Integer affix) { + this.affix = affix; + } + + public String getActiveMenu() { + return activeMenu; + } + + public void setActiveMenu(String activeMenu) { + this.activeMenu = activeMenu; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "MenuVO{" + + "menuId=" + menuId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",parentId=" + parentId + + ",bizType=" + bizType + + ",permission=" + permission + + ",path=" + path + + ",component=" + component + + ",redirect=" + redirect + + ",alwaysShow=" + alwaysShow + + ",hidden=" + hidden + + ",name=" + name + + ",title=" + title + + ",icon=" + icon + + ",noCache=" + noCache + + ",breadcrumb=" + breadcrumb + + ",affix=" + affix + + ",activeMenu=" + activeMenu + + ",seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/MenuPermission.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/MenuPermission.java new file mode 100644 index 00000000..2361763e --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/MenuPermission.java @@ -0,0 +1,120 @@ +package com.mall4j.cloud.rbac.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 菜单资源 + * + * @author FrozenWatermelon + * @date 2020-09-15 16:36:50 + */ +public class MenuPermission extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 菜单资源用户id + */ + private Long menuPermissionId; + + /** + * 资源关联菜单 + */ + private Long menuId; + + /** + * 业务类型 0平台菜单 1 店铺菜单 + */ + private Integer bizType; + + /** + * 权限对应的编码 + */ + private String permission; + + /** + * 资源名称 + */ + private String name; + + /** + * 资源对应服务器路径 + */ + private String uri; + + /** + * 请求方法 1.GET 2.POST 3.PUT 4.DELETE + */ + private Integer method; + + public Long getMenuPermissionId() { + return menuPermissionId; + } + + public void setMenuPermissionId(Long menuPermissionId) { + this.menuPermissionId = menuPermissionId; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Integer getBizType() { + return bizType; + } + + public void setBizType(Integer bizType) { + this.bizType = bizType; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public Integer getMethod() { + return method; + } + + public void setMethod(Integer method) { + this.method = method; + } + + @Override + public String toString() { + return "MenuPermissionVO{" + + "menuPermissionId=" + menuPermissionId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",menuId=" + menuId + + ",bizType=" + bizType + + ",permission=" + permission + + ",name=" + name + + ",uri=" + uri + + ",method=" + method + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Role.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Role.java new file mode 100644 index 00000000..a759be8b --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/Role.java @@ -0,0 +1,104 @@ +package com.mall4j.cloud.rbac.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 角色 + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +public class Role extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * 角色id + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 备注 + */ + private String remark; + + /** + * 创建者ID + */ + private Long createUserId; + + /** + * 业务类型 0平台菜单 1 店铺菜单 + */ + private Integer bizType; + + /** + * 所属租户id + */ + private Long tenantId; + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Long getCreateUserId() { + return createUserId; + } + + public void setCreateUserId(Long createUserId) { + this.createUserId = createUserId; + } + + public Integer getBizType() { + return bizType; + } + + public void setBizType(Integer bizType) { + this.bizType = bizType; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + @Override + public String toString() { + return "Role{" + + "roleId=" + roleId + + ", roleName='" + roleName + '\'' + + ", remark='" + remark + '\'' + + ", createUserId=" + createUserId + + ", bizType=" + bizType + + ", tenantId=" + tenantId + + "} " + super.toString(); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/RoleMenu.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/RoleMenu.java new file mode 100644 index 00000000..7bd98268 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/RoleMenu.java @@ -0,0 +1,74 @@ +package com.mall4j.cloud.rbac.model; + +import com.mall4j.cloud.common.model.BaseModel; + +/** + * 角色与菜单对应关系 + * + * @author FrozenWatermelon + * @date 2020/6/24 + */ +public class RoleMenu extends BaseModel { + + /** + * 关联id + */ + private Long id; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 菜单资源用户id + */ + private Long menuPermissionId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Long getMenuPermissionId() { + return menuPermissionId; + } + + public void setMenuPermissionId(Long menuPermissionId) { + this.menuPermissionId = menuPermissionId; + } + + @Override + public String toString() { + return "RoleMenu{" + + "id=" + id + + ", roleId=" + roleId + + ", menuId=" + menuId + + ", menuPermissionId=" + menuPermissionId + + "} " + super.toString(); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/UserRole.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/UserRole.java new file mode 100644 index 00000000..81a9a7ca --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/model/UserRole.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.rbac.model; + +import com.mall4j.cloud.common.model.BaseModel; + +/** + * 用户与角色对应关系 + * + * @author FrozenWatermelon + * @date 2020/6/24 + */ +public class UserRole extends BaseModel { + + /** + * 关联id + */ + private Long id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + @Override + public String toString() { + return "UserRole{" + "id=" + id + ", userId=" + userId + ", roleId=" + roleId + "} " + super.toString(); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuPermissionService.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuPermissionService.java new file mode 100644 index 00000000..aeb3e691 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuPermissionService.java @@ -0,0 +1,87 @@ +package com.mall4j.cloud.rbac.service; + +import com.mall4j.cloud.api.rbac.bo.UriPermissionBO; + +import java.util.List; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.rbac.model.MenuPermission; +import com.mall4j.cloud.rbac.vo.MenuPermissionVO; + +/** + * 菜单资源 + * + * @author FrozenWatermelon + * @date 2020-09-14 16:27:55 + */ +public interface MenuPermissionService { + + /** + * 分页获取菜单资源列表 + * @param pageDTO 分页参数 + * @param sysType 系统类型 + * @return 菜单资源列表分页数据 + */ + PageVO page(PageDTO pageDTO,Integer sysType); + + /** + * 根据菜单资源id获取菜单资源 + * + * @param menuPermissionId 菜单资源id + * @return 菜单资源 + */ + MenuPermissionVO getByMenuPermissionId(Long menuPermissionId); + + /** + * 保存菜单资源 + * @param menuPermission 菜单资源 + * @return + */ + ServerResponseEntity save(MenuPermission menuPermission); + + /** + * 更新菜单资源 + * @param menuPermission 菜单资源 + * @return + */ + ServerResponseEntity update(MenuPermission menuPermission); + + /** + * 根据菜单资源id删除菜单资源 + * @param menuPermissionId + * @param sysType + */ + void deleteById(Long menuPermissionId, Integer sysType); + + /** + * 根据用户所在系统的用户id,用户所在系统类型,获取用户的权限列表 + * @param userId 用户id + * @param sysType 系统类型 + * @param isAdmin 是否为管理员 + * @return 权限列表 + */ + List listUserPermissions(Long userId, Integer sysType, boolean isAdmin); + + /** + * 清除用户权限缓存 + * @param userId 用户id + * @param sysType 系统类型 + */ + void clearUserPermissionsCache(Long userId, Integer sysType); + + /** + * 根据系统类型,获取该类型用户拥有的所有权限数据 + * @param sysType 系统类型 + * @return uri权限列表 + */ + List listUriPermissionInfo(Integer sysType); + + /** + * 根据menuId获取菜单资源列表 + * @param menuId 菜单id + * @return 菜单资源列表数据 + */ + List listByMenuId(Long menuId); + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuService.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuService.java new file mode 100644 index 00000000..fdd090cd --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/MenuService.java @@ -0,0 +1,65 @@ +package com.mall4j.cloud.rbac.service; + +import com.mall4j.cloud.rbac.model.Menu; + +import java.util.List; + +import com.mall4j.cloud.rbac.vo.MenuSimpleVO; +import com.mall4j.cloud.rbac.vo.MenuVO; + +/** + * 菜单管理 + * + * @author FrozenWatermelon + * @date 2020-09-14 16:27:55 + */ +public interface MenuService { + + /** + * 根据菜单管理id获取菜单管理 + * + * @param menuId 菜单管理id + * @return 菜单管理 + */ + MenuVO getByMenuId(Long menuId); + + /** + * 保存菜单管理 + * @param menu 菜单管理 + */ + void save(Menu menu); + + /** + * 更新菜单管理 + * @param menu 菜单管理 + */ + void update(Menu menu); + + /** + * 根据菜单管理id删除菜单管理 + * @param menuId 菜单id + * @param sysType 系统类型 + */ + void deleteById(Long menuId, Integer sysType); + + /** + * 根据系统类型获取该系统的菜单列表 + * @param sysType 系统类型 + * @return 菜单列表 + */ + List listBySysType(Integer sysType); + + /** + * 根据系统类型获取该系统的菜单列表 + 菜单下的权限列表 + * @param sysType 系统类型 + * @return 菜单列表 + 菜单下的权限列表 + */ + List listWithPermissions(Integer sysType); + + /** + * 获取当前用户可见的菜单ids + * @param userId 用户id + * @return 菜单列表 + */ + List listMenuIds(Long userId); +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleMenuService.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleMenuService.java new file mode 100644 index 00000000..f46994fb --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleMenuService.java @@ -0,0 +1,9 @@ +package com.mall4j.cloud.rbac.service; + +/** + * @author FrozenWatermelon + * @date 2020/6/23 + */ +public interface RoleMenuService { + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleService.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleService.java new file mode 100644 index 00000000..b0880e64 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/RoleService.java @@ -0,0 +1,73 @@ +package com.mall4j.cloud.rbac.service; + +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.rbac.model.Role; +import com.mall4j.cloud.rbac.vo.RoleVO; + +import java.util.List; + +/** + * 角色 + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +public interface RoleService { + + /** + * 分页获取角色列表 + * @param pageDTO 分页参数 + * @param sysType + * @param tenantId + * @return 角色列表分页数据 + */ + PageVO page(PageDTO pageDTO, Integer sysType, Long tenantId); + + /** + * 分页获取角色列表 + * @param sysType 系统类型 + * @param tenantId 租户id + * @return 角色列表分页数据 + */ + List list(Integer sysType, Long tenantId); + + /** + * 根据角色id获取角色 + * + * @param roleId 角色id + * @return 角色 + */ + RoleVO getByRoleId(Long roleId); + + /** + * 保存角色 + * @param role 角色 + * @param menuIds 菜单id列表 + * @param menuPermissionIds 权限id列表 + */ + void save(Role role, List menuIds, List menuPermissionIds); + + /** + * 更新角色 + * @param role 角色 + * @param menuIds 菜单id列表 + * @param menuPermissionIds 权限id列表 + */ + void update(Role role, List menuIds, List menuPermissionIds); + + /** + * 根据角色id删除角色 + * @param roleId + * @param sysType + */ + void deleteById(Long roleId, Integer sysType); + + /** + * 根据角色id获取该角色所在系统 + * @param roleId 角色id + * @return sysType + */ + Integer getBizType(Long roleId); + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/UserRoleService.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/UserRoleService.java new file mode 100644 index 00000000..900dab45 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/UserRoleService.java @@ -0,0 +1,9 @@ +package com.mall4j.cloud.rbac.service; + +/** + * @author FrozenWatermelon + * @date 2020/6/23 + */ +public interface UserRoleService { + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuPermissionServiceImpl.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuPermissionServiceImpl.java new file mode 100644 index 00000000..949c0cbc --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuPermissionServiceImpl.java @@ -0,0 +1,133 @@ +package com.mall4j.cloud.rbac.service.impl; + +import com.mall4j.cloud.api.rbac.bo.UriPermissionBO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.CacheManagerUtil; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.rbac.mapper.MenuPermissionMapper; +import com.mall4j.cloud.rbac.model.MenuPermission; +import com.mall4j.cloud.rbac.service.MenuPermissionService; +import com.mall4j.cloud.rbac.vo.MenuPermissionVO; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * 菜单资源 + * + * @author FrozenWatermelon + * @date 2020-09-14 16:27:55 + */ +@Service +public class MenuPermissionServiceImpl implements MenuPermissionService { + + @Autowired + private MenuPermissionMapper menuPermissionMapper; + + @Autowired + private CacheManagerUtil cacheManagerUtil; + + @Override + public PageVO page(PageDTO pageDTO,Integer sysType) { + return PageUtil.doPage(pageDTO, () -> menuPermissionMapper.list(sysType)); + } + + @Override + public MenuPermissionVO getByMenuPermissionId(Long menuPermissionId) { + return menuPermissionMapper.getByMenuPermissionId(menuPermissionId); + } + + @Override + @CacheEvict(cacheNames = CacheNames.URI_PERMISSION_KEY, key = "#menuPermission.bizType") + public ServerResponseEntity save(MenuPermission menuPermission) { + + MenuPermission dbMenuPermission = menuPermissionMapper.getByPermission(menuPermission.getPermission(), AuthUserContext.get().getSysType()); + if (dbMenuPermission != null) { + return ServerResponseEntity.showFailMsg("权限编码已存在,请勿重复添加"); + } + menuPermissionMapper.save(menuPermission); + return ServerResponseEntity.success(); + } + + @Override + @CacheEvict(cacheNames = CacheNames.URI_PERMISSION_KEY, key = "#menuPermission.bizType") + public ServerResponseEntity update(MenuPermission menuPermission) { + MenuPermission dbMenuPermission = menuPermissionMapper.getByPermission(menuPermission.getPermission(),AuthUserContext.get().getSysType()); + if (dbMenuPermission != null && !Objects.equals(menuPermission.getMenuPermissionId(), dbMenuPermission.getMenuPermissionId())) { + return ServerResponseEntity.showFailMsg("权限编码已存在,请勿重复添加"); + } + menuPermissionMapper.update(menuPermission); + return ServerResponseEntity.success(); + } + + @Override + @CacheEvict(cacheNames = CacheNames.URI_PERMISSION_KEY, key = "#sysType") + public void deleteById(Long menuPermissionId, Integer sysType) { + menuPermissionMapper.deleteById(menuPermissionId,sysType); + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.USER_PERMISSIONS_KEY, key = "#sysType + ':' + #userId"), + @CacheEvict(cacheNames = CacheNames.MENU_ID_LIST_KEY, key = "#userId") + }) + public void clearUserPermissionsCache(Long userId, Integer sysType) { + } + + @Override + public List listUserPermissions(Long userId, Integer sysType, boolean isAdmin) { + MenuPermissionServiceImpl menuPermissionService = (MenuPermissionServiceImpl)AopContext.currentProxy(); + List permsList; + + // 系统管理员,拥有最高权限 + if (isAdmin) { + permsList = menuPermissionService.listAllPermission(sysType); + } + else { + permsList = menuPermissionService.listPermissionByUserIdAndSysType(userId, sysType); + } + return permsList; + } + + @Override + @Cacheable(cacheNames = CacheNames.URI_PERMISSION_KEY, key = "#sysType") + public List listUriPermissionInfo(Integer sysType) { + return menuPermissionMapper.listUriPermissionInfo(sysType); + } + + @Override + public List listByMenuId(Long menuId) { + return menuPermissionMapper.listByMenuId(menuId); + } + + /** + * 获取某个类型用户的所有权限列表(有缓存) + * @param sysType 系统类型 + * @return 权限列表 + */ + @Cacheable(cacheNames = CacheNames.PERMISSIONS_KEY, key = "#sysType") + public List listAllPermission(Integer sysType) { + return menuPermissionMapper.listAllPermissionBySysType(sysType); + } + + /** + * 获取某个类型用户的所有权限列表(有缓存) + * @param sysType 系统类型 + * @return 权限列表 + */ + @Cacheable(cacheNames = CacheNames.USER_PERMISSIONS_KEY, key = "#sysType + ':' + #userId") + public List listPermissionByUserIdAndSysType(Long userId, Integer sysType) { + return menuPermissionMapper.listPermissionByUserIdAndSysType(userId, sysType); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuServiceImpl.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuServiceImpl.java new file mode 100644 index 00000000..99b8708c --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/MenuServiceImpl.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.rbac.service.impl; + +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.rbac.mapper.MenuMapper; +import com.mall4j.cloud.rbac.model.Menu; +import com.mall4j.cloud.rbac.service.MenuService; +import com.mall4j.cloud.rbac.vo.MenuSimpleVO; +import com.mall4j.cloud.rbac.vo.MenuVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 菜单管理 + * + * @author FrozenWatermelon + * @date 2020-09-14 16:27:55 + */ +@Service +public class MenuServiceImpl implements MenuService { + + @Autowired + private MenuMapper menuMapper; + + @Override + public MenuVO getByMenuId(Long menuId) { + return menuMapper.getByMenuId(menuId); + } + + @Override + @CacheEvict(cacheNames = CacheNames.MENU_LIST_KEY, key = "#menu.bizType") + public void save(Menu menu) { + menuMapper.save(menu); + } + + @Override + @CacheEvict(cacheNames = CacheNames.MENU_LIST_KEY, key = "#menu.bizType") + public void update(Menu menu) { + menuMapper.update(menu); + } + + @Override + @CacheEvict(cacheNames = CacheNames.MENU_LIST_KEY, key = "#sysType") + public void deleteById(Long menuId, Integer sysType) { + menuMapper.deleteById(menuId,sysType); + } + + @Override + @Cacheable(cacheNames = CacheNames.MENU_LIST_KEY, key = "#sysType") + public List listBySysType(Integer sysType) { + return menuMapper.listBySysType(sysType); + } + + @Override + public List listWithPermissions(Integer sysType) { + return menuMapper.listWithPermissions(sysType); + } + + @Override + @Cacheable(cacheNames = CacheNames.MENU_ID_LIST_KEY, key = "#userId") + public List listMenuIds(Long userId) { + return menuMapper.listMenuIds(userId); + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleMenuServiceImpl.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleMenuServiceImpl.java new file mode 100644 index 00000000..44f9e78e --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleMenuServiceImpl.java @@ -0,0 +1,18 @@ +package com.mall4j.cloud.rbac.service.impl; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import com.mall4j.cloud.rbac.mapper.RoleMenuMapper; +import com.mall4j.cloud.rbac.service.RoleMenuService; + +/** + * @author FrozenWatermelon + * @date 2020/6/23 + */ +@Service +public class RoleMenuServiceImpl implements RoleMenuService { + + @Resource + private RoleMenuMapper roleMenuMapper; + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleServiceImpl.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleServiceImpl.java new file mode 100644 index 00000000..ec9143aa --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/RoleServiceImpl.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.rbac.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.rbac.mapper.RoleMenuMapper; +import com.mall4j.cloud.rbac.mapper.UserRoleMapper; +import com.mall4j.cloud.rbac.model.Role; +import com.mall4j.cloud.rbac.mapper.RoleMapper; +import com.mall4j.cloud.rbac.model.RoleMenu; +import com.mall4j.cloud.rbac.service.RoleService; +import com.mall4j.cloud.rbac.vo.RoleVO; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 角色 + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +@Service +public class RoleServiceImpl implements RoleService { + + @Autowired + private RoleMapper roleMapper; + + @Autowired + private RoleMenuMapper roleMenuMapper; + + @Autowired + private UserRoleMapper userRoleMapper; + + @Override + public PageVO page(PageDTO pageDTO, Integer sysType, Long tenantId) { + return PageUtil.doPage(pageDTO, () -> roleMapper.list(sysType, tenantId)); + } + + @Override + public List list(Integer sysType, Long tenantId) { + return roleMapper.list(sysType, tenantId); + } + + @Override + public RoleVO getByRoleId(Long roleId) { + RoleVO role = roleMapper.getByRoleId(roleId); + List roleMenus = roleMenuMapper.getByRoleId(roleId); + role.setMenuIds(roleMenus.stream().filter(roleMenu -> roleMenu.getMenuId() != null).map(RoleMenu::getMenuId).collect(Collectors.toList())); + role.setMenuPermissionIds(roleMenus.stream().filter(roleMenu -> roleMenu.getMenuPermissionId() != null).map(RoleMenu::getMenuPermissionId).collect(Collectors.toList())); + return role; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(Role role, List menuIds, List menuPermissionIds) { + roleMapper.save(role); + insertMenuAndPermission(role.getRoleId(), menuIds, menuPermissionIds); + } + + + @Override + public void update(Role role, List menuIds, List menuPermissionIds) { + roleMapper.update(role); + roleMenuMapper.deleteByRoleId(role.getRoleId()); + insertMenuAndPermission(role.getRoleId(), menuIds, menuPermissionIds); + } + + private void insertMenuAndPermission(Long roleId, List menuIds, List menuPermissionIds) { + if (CollectionUtil.isNotEmpty(menuIds)) { + List roleMenus = menuIds.stream().map(menuId -> { + RoleMenu roleMenu = new RoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menuId); + return roleMenu; + }).collect(Collectors.toList()); + roleMenuMapper.insertBatch(roleMenus); + } + + if (CollectionUtil.isNotEmpty(menuPermissionIds)) { + List roleMenus = menuPermissionIds.stream().map(menuPermissionId -> { + RoleMenu roleMenu = new RoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuPermissionId(menuPermissionId); + return roleMenu; + }).collect(Collectors.toList()); + roleMenuMapper.insertBatch(roleMenus); + } + } + + @Override + public void deleteById(Long roleId, Integer sysType) { + roleMapper.deleteById(roleId,sysType); + roleMenuMapper.deleteByRoleId(roleId); + userRoleMapper.deleteByRoleId(roleId); + } + + @Override + public Integer getBizType(Long roleId) { + return roleMapper.getBizType(roleId); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleFeignController.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleFeignController.java new file mode 100644 index 00000000..7f37f289 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleFeignController.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.rbac.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.rbac.dto.UserRoleDTO; +import com.mall4j.cloud.api.rbac.feign.UserRoleFeignClient; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.rbac.mapper.UserRoleMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/7/15 + */ +@RestController +public class UserRoleFeignController implements UserRoleFeignClient { + + private static final Logger logger = LoggerFactory.getLogger(UserRoleFeignController.class); + + @Autowired + private UserRoleMapper userRoleMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.MENU_ID_LIST_KEY, key = "#userRoleDTO.userId") + public ServerResponseEntity saveByUserIdAndSysType(UserRoleDTO userRoleDTO) { + + if(CollUtil.isEmpty(userRoleDTO.getRoleIds())){ + return ServerResponseEntity.success(); + } + //保存用户与角色关系 + userRoleMapper.insertUserAndUserRole(userRoleDTO.getUserId(), userRoleDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.MENU_ID_LIST_KEY, key = "#userRoleDTO.userId") + public ServerResponseEntity updateByUserIdAndSysType(UserRoleDTO userRoleDTO) { + //先删除用户与角色关系 + userRoleMapper.deleteByUserId(userRoleDTO.getUserId()); + + if(CollUtil.isEmpty(userRoleDTO.getRoleIds())){ + return ServerResponseEntity.success(); + } + //保存用户与角色关系 + userRoleMapper.insertUserAndUserRole(userRoleDTO.getUserId(), userRoleDTO.getRoleIds()); + return ServerResponseEntity.success(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(cacheNames = CacheNames.MENU_ID_LIST_KEY, key = "#userId") + public ServerResponseEntity deleteByUserIdAndSysType(Long userId) { + userRoleMapper.deleteByUserId(userId); + return ServerResponseEntity.success(); + } + + @Override + public ServerResponseEntity> getRoleIds(Long userId) { + return ServerResponseEntity.success(userRoleMapper.getRoleIds(userId)); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleServiceImpl.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleServiceImpl.java new file mode 100644 index 00000000..246203e9 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/service/impl/UserRoleServiceImpl.java @@ -0,0 +1,18 @@ +package com.mall4j.cloud.rbac.service.impl; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import com.mall4j.cloud.rbac.mapper.UserRoleMapper; +import com.mall4j.cloud.rbac.service.UserRoleService; + +/** + * @author FrozenWatermelon + * @date 2020/6/23 + */ +@Service +public class UserRoleServiceImpl implements UserRoleService { + + @Resource + private UserRoleMapper userRoleMapper; + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionSimpleVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionSimpleVO.java new file mode 100644 index 00000000..873b4cae --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionSimpleVO.java @@ -0,0 +1,55 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 菜单资源简易VO + * + * @author FrozenWatermelon + * @date 2020-09-17 16:35:01 + */ +public class MenuPermissionSimpleVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单资源用户id") + private Long menuPermissionId; + + @ApiModelProperty("资源关联菜单") + private Long menuId; + + @ApiModelProperty("资源名称") + private String name; + + public Long getMenuPermissionId() { + return menuPermissionId; + } + + public void setMenuPermissionId(Long menuPermissionId) { + this.menuPermissionId = menuPermissionId; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "MenuPermissionVO{" + + "menuPermissionId=" + menuPermissionId + + ",menuId=" + menuId + + ",name=" + name + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionVO.java new file mode 100644 index 00000000..2418d404 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuPermissionVO.java @@ -0,0 +1,103 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 菜单资源VO + * + * @author FrozenWatermelon + * @date 2020-09-15 16:35:01 + */ +public class MenuPermissionVO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单资源用户id") + private Long menuPermissionId; + + @ApiModelProperty("资源关联菜单") + private Long menuId; + + @ApiModelProperty("菜单标题") + private String menuTitle; + + @ApiModelProperty("权限对应的编码") + private String permission; + + @ApiModelProperty("资源名称") + private String name; + + @ApiModelProperty("资源对应服务器路径") + private String uri; + + @ApiModelProperty("请求方法 1.GET 2.POST 3.PUT 4.DELETE") + private Integer method; + + public Long getMenuPermissionId() { + return menuPermissionId; + } + + public void setMenuPermissionId(Long menuPermissionId) { + this.menuPermissionId = menuPermissionId; + } + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public Integer getMethod() { + return method; + } + + public void setMethod(Integer method) { + this.method = method; + } + + public String getMenuTitle() { + return menuTitle; + } + + public void setMenuTitle(String menuTitle) { + this.menuTitle = menuTitle; + } + + @Override + public String toString() { + return "MenuPermissionVO{" + + "menuPermissionId=" + menuPermissionId + + ", menuId=" + menuId + + ", menuTitle='" + menuTitle + '\'' + + ", permission='" + permission + '\'' + + ", name='" + name + '\'' + + ", uri='" + uri + '\'' + + ", method=" + method + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuSimpleVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuSimpleVO.java new file mode 100644 index 00000000..b307b310 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuSimpleVO.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * 菜单管理VO + * + * @author FrozenWatermelon + * @date 2020-09-15 16:35:01 + */ +public class MenuSimpleVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单id") + private Long menuId; + + @ApiModelProperty("父菜单ID,一级菜单为0") + private Long parentId; + + @ApiModelProperty("设置该路由在侧边栏和面包屑中展示的名字") + private String title; + + @ApiModelProperty("菜单权限列表") + private List menuPermissions; + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public List getMenuPermissions() { + return menuPermissions; + } + + public void setMenuPermissions(List menuPermissions) { + this.menuPermissions = menuPermissions; + } + + @Override + public String toString() { + return "MenuSimpleVO{" + + "menuId=" + menuId + + ", parentId=" + parentId + + ", title='" + title + '\'' + + ", menuPermissions=" + menuPermissions + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuVO.java new file mode 100644 index 00000000..7e92b749 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/MenuVO.java @@ -0,0 +1,211 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * 菜单管理VO + * + * @author FrozenWatermelon + * @date 2020-09-15 16:35:01 + */ +public class MenuVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("菜单id") + private Long menuId; + + @ApiModelProperty("父菜单ID,一级菜单为0") + private Long parentId; + + @ApiModelProperty("权限,需要有哪个权限才能访问该菜单") + private String permission; + + @ApiModelProperty("路径 就像uri") + private String path; + + @ApiModelProperty("组件如:1.'Layout' 为布局,不会跳页面 2.'components-demo/tinymce' 跳转到该页面") + private String component; + + @ApiModelProperty("当设置 noRedirect 的时候该路由在面包屑导航中不可被点击") + private String redirect; + + @ApiModelProperty("一直显示根路由") + private Integer alwaysShow; + + @ApiModelProperty("当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1") + private Integer hidden; + + @ApiModelProperty("设定路由的名字,一定要填写不然使用时会出现各种问题") + private String name; + + @ApiModelProperty("设置该路由在侧边栏和面包屑中展示的名字") + private String title; + + @ApiModelProperty("设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon") + private String icon; + + @ApiModelProperty("如果设置为true,则不会被 缓存(默认 false)") + private Integer noCache; + + @ApiModelProperty("如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)") + private Integer breadcrumb; + + @ApiModelProperty("若果设置为true,它则会固定在tags-view中(默认 false)") + private Integer affix; + + @ApiModelProperty("当路由设置了该属性,则会高亮相对应的侧边栏。") + private String activeMenu; + + @ApiModelProperty("排序,越小越靠前") + private Integer seq; + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getRedirect() { + return redirect; + } + + public void setRedirect(String redirect) { + this.redirect = redirect; + } + + public Integer getAlwaysShow() { + return alwaysShow; + } + + public void setAlwaysShow(Integer alwaysShow) { + this.alwaysShow = alwaysShow; + } + + public Integer getHidden() { + return hidden; + } + + public void setHidden(Integer hidden) { + this.hidden = hidden; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public Integer getNoCache() { + return noCache; + } + + public void setNoCache(Integer noCache) { + this.noCache = noCache; + } + + public Integer getBreadcrumb() { + return breadcrumb; + } + + public void setBreadcrumb(Integer breadcrumb) { + this.breadcrumb = breadcrumb; + } + + public Integer getAffix() { + return affix; + } + + public void setAffix(Integer affix) { + this.affix = affix; + } + + public String getActiveMenu() { + return activeMenu; + } + + public void setActiveMenu(String activeMenu) { + this.activeMenu = activeMenu; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "MenuVO{" + + "menuId=" + menuId + + ",parentId=" + parentId + + ",permission=" + permission + + ",path=" + path + + ",component=" + component + + ",redirect=" + redirect + + ",alwaysShow=" + alwaysShow + + ",hidden=" + hidden + + ",name=" + name + + ",title=" + title + + ",icon=" + icon + + ",noCache=" + noCache + + ",breadcrumb=" + breadcrumb + + ",affix=" + affix + + ",activeMenu=" + activeMenu + + ",seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RoleVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RoleVO.java new file mode 100644 index 00000000..6036d5e1 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RoleVO.java @@ -0,0 +1,118 @@ +package com.mall4j.cloud.rbac.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * 角色VO + * + * @author FrozenWatermelon + * @date 2020-09-17 19:15:44 + */ +public class RoleVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("角色id") + private Long roleId; + + @ApiModelProperty("角色名称") + private String roleName; + + @ApiModelProperty("备注") + private String remark; + + @ApiModelProperty("创建者ID") + private Long createUserId; + + @ApiModelProperty("所属租户id") + private Long tenantId; + + @ApiModelProperty("类型") + private Integer bizType; + + @ApiModelProperty("菜单id列表") + private List menuIds; + + @ApiModelProperty("菜单资源id列表") + private List menuPermissionIds; + + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Long getCreateUserId() { + return createUserId; + } + + public void setCreateUserId(Long createUserId) { + this.createUserId = createUserId; + } + + public List getMenuIds() { + return menuIds; + } + + public void setMenuIds(List menuIds) { + this.menuIds = menuIds; + } + + public List getMenuPermissionIds() { + return menuPermissionIds; + } + + public void setMenuPermissionIds(List menuPermissionIds) { + this.menuPermissionIds = menuPermissionIds; + } + + public Long getTenantId() { + return tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + public Integer getBizType() { + return bizType; + } + + public void setBizType(Integer bizType) { + this.bizType = bizType; + } + + @Override + public String toString() { + return "RoleVO{" + + "roleId=" + roleId + + ", roleName='" + roleName + '\'' + + ", remark='" + remark + '\'' + + ", createUserId=" + createUserId + + ", tenantId=" + tenantId + + ", bizType=" + bizType + + ", menuIds=" + menuIds + + ", menuPermissionIds=" + menuPermissionIds + + "} " + super.toString(); + } +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteMetaVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteMetaVO.java new file mode 100644 index 00000000..bb43e123 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteMetaVO.java @@ -0,0 +1,115 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/8/7 + */ +public class RouteMetaVO { + + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + @ApiModelProperty("设置该路由在侧边栏和面包屑中展示的名字") + private String title; + + /** + * 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon + */ + @ApiModelProperty("设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon") + private String icon; + + /** + * 如果设置为true,则不会被 缓存(默认 false) + */ + @ApiModelProperty("如果设置为true,则不会被 缓存(默认 false)") + private Boolean noCache; + + /** + * 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) + */ + @ApiModelProperty("如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)") + private Boolean breadcrumb; + + /** + * 若果设置为true,它则会固定在tags-view中(默认 false) + */ + @ApiModelProperty("若果设置为true,它则会固定在tags-view中(默认 false)") + private Boolean affix; + + /** + * 当路由设置了该属性,则会高亮相对应的侧边栏。 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list + * 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置 + */ + @ApiModelProperty("当路由设置了该属性,则会高亮相对应的侧边栏。") + private String activeMenu; + + @ApiModelProperty("需要什么权限才能访问该菜单") + private List roles; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public Boolean getNoCache() { + return noCache; + } + + public void setNoCache(Boolean noCache) { + this.noCache = noCache; + } + + public Boolean getBreadcrumb() { + return breadcrumb; + } + + public void setBreadcrumb(Boolean breadcrumb) { + this.breadcrumb = breadcrumb; + } + + public Boolean getAffix() { + return affix; + } + + public void setAffix(Boolean affix) { + this.affix = affix; + } + + public String getActiveMenu() { + return activeMenu; + } + + public void setActiveMenu(String activeMenu) { + this.activeMenu = activeMenu; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "RouteMetaVO{" + "title='" + title + '\'' + ", icon='" + icon + '\'' + ", noCache=" + noCache + + ", breadcrumb=" + breadcrumb + ", affix=" + affix + ", activeMenu='" + activeMenu + '\'' + ", roles=" + + roles + '}'; + } + +} diff --git a/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteVO.java b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteVO.java new file mode 100644 index 00000000..9150a557 --- /dev/null +++ b/mall4cloud-rbac/src/main/java/com/mall4j/cloud/rbac/vo/RouteVO.java @@ -0,0 +1,161 @@ +package com.mall4j.cloud.rbac.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2020/8/6 + */ +public class RouteVO { + + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("parentId") + private Long parentId; + + /** + * 就像uri + */ + @ApiModelProperty("路径: 就像uri") + private String path; + + /** + * 组件: 'layout/Layout' 为布局,不会跳页面 'views/components-demo/tinymce' 跳转到该页面 + */ + @ApiModelProperty("组件如:1.'Layout' 为布局,不会跳页面 2.'components-demo/tinymce' 跳转到该页面") + private String component; + + /** + * 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + @ApiModelProperty("当设置 noRedirect 的时候该路由在面包屑导航中不可被点击") + private String redirect; + + /** + * 一直显示根路由 + */ + @ApiModelProperty("一直显示根路由") + private Boolean alwaysShow; + + /** + * 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + */ + @ApiModelProperty("当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1") + private Boolean hidden; + + /** + * 设定路由的名字,一定要填写不然使用时会出现各种问题 + */ + @ApiModelProperty("设定路由的名字,一定要填写不然使用时会出现各种问题") + private String name; + + /** + * 设定路由的名字,一定要填写不然使用时会出现各种问题 + */ + @ApiModelProperty("排序") + private Integer seq; + + /** + * 路由的源信息 + */ + @ApiModelProperty("路由的源信息") + private RouteMetaVO meta; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getRedirect() { + return redirect; + } + + public void setRedirect(String redirect) { + this.redirect = redirect; + } + + public Boolean getAlwaysShow() { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) { + this.alwaysShow = alwaysShow; + } + + public Boolean getHidden() { + return hidden; + } + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RouteMetaVO getMeta() { + return meta; + } + + public void setMeta(RouteMetaVO meta) { + this.meta = meta; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "RouteVO{" + + "id=" + id + + ", parentId=" + parentId + + ", path='" + path + '\'' + + ", component='" + component + '\'' + + ", redirect='" + redirect + '\'' + + ", alwaysShow=" + alwaysShow + + ", hidden=" + hidden + + ", name='" + name + '\'' + + ", seq=" + seq + + ", meta=" + meta + + '}'; + } + +} diff --git a/mall4cloud-rbac/src/main/resources/bootstrap.yml b/mall4cloud-rbac/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..71c5a150 --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 9102 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ + diff --git a/mall4cloud-rbac/src/main/resources/mapper/MenuMapper.xml b/mall4cloud-rbac/src/main/resources/mapper/MenuMapper.xml new file mode 100644 index 00000000..063f6b9c --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/mapper/MenuMapper.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `menu_id`,`create_time`,`update_time`,`parent_id`,`biz_type`,`permission`,`path`,`component`,`redirect`,`always_show`,`hidden`,`name`,`title`,`icon`,`no_cache`,`breadcrumb`,`affix`,`active_menu`,`seq` + + + + + + + insert into menu (`parent_id`,`biz_type`,`permission`,`path`,`component`,`redirect`,`always_show`,`hidden`,`name`,`title`,`icon`,`no_cache`,`breadcrumb`,`affix`,`active_menu`,seq) + values (#{menu.parentId},#{menu.bizType},#{menu.permission},#{menu.path},#{menu.component},#{menu.redirect},#{menu.alwaysShow},#{menu.hidden},#{menu.name},#{menu.title},#{menu.icon},#{menu.noCache},#{menu.breadcrumb},#{menu.affix},#{menu.activeMenu},#{menu.seq}); + + + + + update menu + + + + parent_id = #{menu.parentId}, + + + biz_type = #{menu.bizType}, + + + permission = #{menu.permission}, + + + path = #{menu.path}, + + + component = #{menu.component}, + + + redirect = #{menu.redirect}, + + + always_show = #{menu.alwaysShow}, + + + hidden = #{menu.hidden}, + + + `name` = #{menu.name}, + + + title = #{menu.title}, + + + icon = #{menu.icon}, + + + no_cache = #{menu.noCache}, + + + breadcrumb = #{menu.breadcrumb}, + + + affix = #{menu.affix}, + + + active_menu = #{menu.activeMenu}, + + + seq = #{menu.seq}, + + + where menu_id = #{menu.menuId} + + + + delete from menu where menu_id = #{menuId} and biz_type = #{sysType} + + + + + + + + + diff --git a/mall4cloud-rbac/src/main/resources/mapper/MenuPermissionMapper.xml b/mall4cloud-rbac/src/main/resources/mapper/MenuPermissionMapper.xml new file mode 100644 index 00000000..9e6827b1 --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/mapper/MenuPermissionMapper.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + `menu_permission_id`,`menu_id`,`biz_type`,`permission`,`name`,`uri`,`method` + + + + + + + insert into menu_permission (`menu_id`, `biz_type`, `permission`, `name`, `uri`, `method`) + values (#{menuPermission.menuId}, #{menuPermission.bizType}, #{menuPermission.permission}, + #{menuPermission.name}, #{menuPermission.uri}, #{menuPermission.method}); + + + + + update menu_permission + + + + menu_id = #{menuPermission.menuId}, + + + + + biz_type = #{menuPermission.bizType}, + + + + + permission = #{menuPermission.permission}, + + + + + name = #{menuPermission.name}, + + + + + uri = #{menuPermission.uri}, + + + + + method = #{menuPermission.method}, + + + + where menu_permission_id = #{menuPermission.menuPermissionId} + + + + + delete + from menu_permission + where menu_permission_id = #{menuPermissionId} + and biz_type = #{sysType} + + + + + + + + diff --git a/mall4cloud-rbac/src/main/resources/mapper/RoleMapper.xml b/mall4cloud-rbac/src/main/resources/mapper/RoleMapper.xml new file mode 100644 index 00000000..ce8885da --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/mapper/RoleMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + `role_id`,`create_time`,`update_time`,`role_name`,`remark`,`create_user_id`,`biz_type`, `tenant_id` + + + + + insert into role (`role_name`,`remark`,`create_user_id`,`biz_type`, `tenant_id`) + values (#{role.roleName},#{role.remark},#{role.createUserId},#{role.bizType}, #{role.tenantId}); + + + update role + + + role_name = #{role.roleName}, + + + remark = #{role.remark}, + + + create_user_id = #{role.createUserId}, + + + biz_type = #{role.bizType}, + + + where role_id = #{role.roleId} + + + delete from role where role_id = #{roleId} and biz_type = #{sysType} + + + + diff --git a/mall4cloud-rbac/src/main/resources/mapper/RoleMenuMapper.xml b/mall4cloud-rbac/src/main/resources/mapper/RoleMenuMapper.xml new file mode 100644 index 00000000..bbd4e8cf --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/mapper/RoleMenuMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + insert into role_menu (role_id, menu_id, menu_permission_id) values + + (#{roleMenu.roleId},#{roleMenu.menuId},#{roleMenu.menuPermissionId}) + + + + delete from role_menu where role_id = #{roleId} + + + + diff --git a/mall4cloud-rbac/src/main/resources/mapper/UserRoleMapper.xml b/mall4cloud-rbac/src/main/resources/mapper/UserRoleMapper.xml new file mode 100644 index 00000000..ffdba372 --- /dev/null +++ b/mall4cloud-rbac/src/main/resources/mapper/UserRoleMapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + id, create_time, update_time, user_id, role_id + + + + + + delete + from user_role + where user_id = #{userId} + + + + + + + + insert into user_role (user_id, role_id) values + + + + (#{userId}, #{roleId}) + + + + + delete + from user_role + where role_id = #{roleId} + + diff --git a/mall4cloud-search/pom.xml b/mall4cloud-search/pom.xml new file mode 100644 index 00000000..3916e7d2 --- /dev/null +++ b/mall4cloud-search/pom.xml @@ -0,0 +1,84 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-search + mall4cloud 搜索模块 + jar + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-rocketmq + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-product + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-multishop + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-order + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-search + ${project.version} + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + true + + + + + diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/SearchApplication.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/SearchApplication.java new file mode 100644 index 00000000..2fd8db98 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/SearchApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.search; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/09/24 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class SearchApplication { + + public static void main(String[] args) { + SpringApplication.run(SearchApplication.class, args); + } + +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/BrandBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/BrandBO.java new file mode 100644 index 00000000..a63027d2 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/BrandBO.java @@ -0,0 +1,75 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +/** + * 品牌信息 + * + * @author YXF + * @date 2020-12-23 15:27:24 + */ +@CanalModel(database = "mall4cloud_product", table = "brand", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class BrandBO { + /** + * brand_id + */ + private Long brandId; + + /** + * 品牌名称 + */ + private String name; + + /** + * 品牌描述 + */ + private String desc; + + /** + * 品牌logo图片 + */ + private String imgUrl; + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + @Override + public String toString() { + return "BrandBO{" + + "brandId=" + brandId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", imgUrl='" + imgUrl + '\'' + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/CategoryBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/CategoryBO.java new file mode 100644 index 00000000..7eabe736 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/CategoryBO.java @@ -0,0 +1,173 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +/** + * 分类信息 + * + * @author YXF + * @date 2020-12-23 15:27:24 + */ +@CanalModel(database = "mall4cloud_product", table = "category", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class CategoryBO { + /** + * 分类id + */ + private Long categoryId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 父ID + */ + private Long parentId; + + /** + * 分类名称 + */ + private String name; + + /** + * 分类描述 + */ + private String desc; + + /** + * 分类地址{parent_id}-{child_id},... + */ + private String path; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + /** + * 分类图标 + */ + private String icon; + + /** + * 分类的显示图片 + */ + private String imgUrl; + + /** + * 分类层级 从0开始 + */ + private Integer level; + + /** + * 排序 + */ + private Integer seq; + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "CategoryBO{" + + "categoryId=" + categoryId + + ", shopId=" + shopId + + ", parentId=" + parentId + + ", name='" + name + '\'' + + ", desc='" + desc + '\'' + + ", path='" + path + '\'' + + ", status=" + status + + ", icon='" + icon + '\'' + + ", imgUrl='" + imgUrl + '\'' + + ", level=" + level + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/OrderBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/OrderBO.java new file mode 100644 index 00000000..6185e083 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/OrderBO.java @@ -0,0 +1,232 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +import java.util.Date; + +/** + * 商品信息 + * + * @author YXF + * @date 2020-12-23 15:27:24 + */ +@CanalModel(database = "mall4cloud_order", table = "order", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class OrderBO { + + /** + * 订单ID + */ + private Long orderId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 店铺名称 + */ + private String shopName; + + /** + * 总值 + */ + private Long total; + + /** + * 订单状态 1:待付款 2:待发货3:待收货(已发货) 5:成功 6:失败 + */ + private Integer status; + + /** + * 配送类型 3:无需快递 + */ + private Integer deliveryType; + + /** + * 订单关闭原因 1-超时未支付 4-买家取消 15-已通过货到付款交易 + */ + private Integer closeType; + + /** + * 订单商品总数 + */ + private Integer allCount; + + /** + * 付款时间 + */ + private Date payTime; + + /** + * 发货时间 + */ + private Date deliveryTime; + + /** + * 完成时间 + */ + private Date finallyTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 是否已支付,1.已支付0.未支付 + */ + private Integer isPayed; + + /** + * 用户订单删除状态,0:没有删除, 1:回收站, 2:永久删除 + */ + private Integer deleteStatus; + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getDeliveryType() { + return deliveryType; + } + + public void setDeliveryType(Integer deliveryType) { + this.deliveryType = deliveryType; + } + + public Integer getCloseType() { + return closeType; + } + + public void setCloseType(Integer closeType) { + this.closeType = closeType; + } + + public Integer getAllCount() { + return allCount; + } + + public void setAllCount(Integer allCount) { + this.allCount = allCount; + } + + public Date getPayTime() { + return payTime; + } + + public void setPayTime(Date payTime) { + this.payTime = payTime; + } + + public Date getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(Date deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Date getFinallyTime() { + return finallyTime; + } + + public void setFinallyTime(Date finallyTime) { + this.finallyTime = finallyTime; + } + + public Date getCancelTime() { + return cancelTime; + } + + public void setCancelTime(Date cancelTime) { + this.cancelTime = cancelTime; + } + + public Integer getIsPayed() { + return isPayed; + } + + public void setIsPayed(Integer isPayed) { + this.isPayed = isPayed; + } + + public Integer getDeleteStatus() { + return deleteStatus; + } + + public void setDeleteStatus(Integer deleteStatus) { + this.deleteStatus = deleteStatus; + } + + @Override + public String toString() { + return "OrderBO{" + + "orderId=" + orderId + + ", shopId=" + shopId + + ", userId=" + userId + + ", shopName='" + shopName + '\'' + + ", total=" + total + + ", status=" + status + + ", deliveryType=" + deliveryType + + ", closeType=" + closeType + + ", allCount=" + allCount + + ", payTime=" + payTime + + ", deliveryTime=" + deliveryTime + + ", finallyTime=" + finallyTime + + ", cancelTime=" + cancelTime + + ", isPayed=" + isPayed + + ", deleteStatus=" + deleteStatus + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/ShopDetailBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/ShopDetailBO.java new file mode 100644 index 00000000..94f3e14d --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/ShopDetailBO.java @@ -0,0 +1,89 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +/** + * 品牌信息 + * + * @author YXF + * @date 2020-12-23 15:27:24 + */ +@CanalModel(database = "mall4cloud_multishop", table = "shop_detail", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class ShopDetailBO { + /** + * 店铺id + */ + private Long shopId; + + /** + * 店铺类型1自营店 2普通店 + */ + private Integer type; + + /** + * 店铺名称 + */ + private String shopName; + + /** + * 店铺logo(可修改) + */ + private String shopLogo; + + /** + * 店铺状态(-1:未开通 0: 停业中 1:营业中 2:平台下线 3:平台下线待审核) + */ + private Integer shopStatus; + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public String getShopLogo() { + return shopLogo; + } + + public void setShopLogo(String shopLogo) { + this.shopLogo = shopLogo; + } + + public Integer getShopStatus() { + return shopStatus; + } + + public void setShopStatus(Integer shopStatus) { + this.shopStatus = shopStatus; + } + + @Override + public String toString() { + return "ShopDetail{" + + "shopId=" + shopId + + ",type=" + type + + ",shopName=" + shopName + + ",shopLogo=" + shopLogo + + ",shopStatus=" + shopStatus + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuBO.java new file mode 100644 index 00000000..0997d962 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuBO.java @@ -0,0 +1,212 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +/** + * 商品信息 + * + * @author YXF + * @date 2020-12-23 15:27:24 + */ +@CanalModel(database = "mall4cloud_product", table = "spu", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class SpuBO { + /** + * spu id + */ + private Long spuId; + + /** + * 品牌ID + */ + private Long brandId; + + /** + * 分类ID + */ + private Long categoryId; + + /** + * 店铺id + */ + private Long shopId; + + /** + * spu名称 + */ + private String name; + + /** + * 卖点 + */ + private String sellingPoint; + + /** + * 主图 + */ + private String mainImgUrl; + + /** + * 商品图片 多个图片逗号分隔 + */ + private String imgUrls; + + /** + * 商品视频 + */ + private String video; + + /** + * 售价,整数方式保存 + */ + private Long priceFee; + + /** + * 市场价,整数方式保存 + */ + private Long marketPriceFee; + + /** + * 状态 1:enable, 0:disable, -1:deleted + */ + private Integer status; + + private Integer hasSkuImg; + + /** + * 序号 + */ + private Integer seq; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Long getBrandId() { + return brandId; + } + + public void setBrandId(Long brandId) { + this.brandId = brandId; + } + + public Long getCategoryId() { + return categoryId; + } + + public void setCategoryId(Long categoryId) { + this.categoryId = categoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSellingPoint() { + return sellingPoint; + } + + public void setSellingPoint(String sellingPoint) { + this.sellingPoint = sellingPoint; + } + + public String getImgUrls() { + return imgUrls; + } + + public void setImgUrls(String imgUrls) { + this.imgUrls = imgUrls; + } + + public String getVideo() { + return video; + } + + public void setVideo(String video) { + this.video = video; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public Integer getHasSkuImg() { + return hasSkuImg; + } + + public void setHasSkuImg(Integer hasSkuImg) { + this.hasSkuImg = hasSkuImg; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "SpuBO{" + + "spuId=" + spuId + + ", brandId=" + brandId + + ", categoryId=" + categoryId + + ", shopId=" + shopId + + ", name='" + name + '\'' + + ", sellingPoint='" + sellingPoint + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", imgUrls='" + imgUrls + '\'' + + ", video='" + video + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", status=" + status + + ", hasSkuImg=" + hasSkuImg + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuExtensionBO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuExtensionBO.java new file mode 100644 index 00000000..6d3c0089 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/bo/SpuExtensionBO.java @@ -0,0 +1,132 @@ +package com.mall4j.cloud.search.bo; + +import cn.throwx.canal.gule.annotation.CanalModel; +import cn.throwx.canal.gule.common.FieldNamingPolicy; + +import java.util.Date; + +/** + * 商品拓展信息 + * + * @author FrozenWatermelon + */ +@CanalModel(database = "mall4cloud_product", table = "spu_extension", fieldNamingPolicy = FieldNamingPolicy.LOWER_UNDERSCORE) +public class SpuExtensionBO { + /** + * 商品扩展信息表id + */ + private Long spuExtendId; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 商品id + */ + private Long spuId; + + /** + * 销量 + */ + private Integer saleNum; + + /** + * 实际库存 + */ + private Integer actualStock; + + /** + * 锁定库存 + */ + private Integer lockStock; + + /** + * 可售卖库存 + */ + private Integer stock; + + public Long getSpuExtendId() { + return spuExtendId; + } + + public void setSpuExtendId(Long spuExtendId) { + this.spuExtendId = spuExtendId; + } + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public Integer getActualStock() { + return actualStock; + } + + public void setActualStock(Integer actualStock) { + this.actualStock = actualStock; + } + + public Integer getLockStock() { + return lockStock; + } + + public void setLockStock(Integer lockStock) { + this.lockStock = lockStock; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "SpuExtensionBO{" + + "spuExtendId=" + spuExtendId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",spuId=" + spuId + + ",saleNum=" + saleNum + + ",actualStock=" + actualStock + + ",lockStock=" + lockStock + + ",stock=" + stock + + '}'; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinLogEventParser.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinLogEventParser.java new file mode 100644 index 00000000..681746cb --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinLogEventParser.java @@ -0,0 +1,93 @@ +package com.mall4j.cloud.search.canal; + +import cn.throwx.canal.gule.common.BinLogEventType; +import cn.throwx.canal.gule.common.OperationType; +import cn.throwx.canal.gule.model.CanalBinLogEvent; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.parser.BaseCommonEntryFunction; +import cn.throwx.canal.gule.support.parser.BasePrimaryKeyTupleFunction; +import cn.throwx.canal.gule.support.parser.CanalBinLogEventParser; +import cn.throwx.canal.gule.support.parser.DefaultCanalBinLogEventParser; +import com.alibaba.fastjson.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * @author FrozenWatermelon + */ +public class mall4cloudCanalBinLogEventParser implements CanalBinLogEventParser { + + private static final Logger log = LoggerFactory.getLogger(DefaultCanalBinLogEventParser.class); + + @Override + public List> parse(CanalBinLogEvent event, Class klass, BasePrimaryKeyTupleFunction primaryKeyFunction, BaseCommonEntryFunction commonEntryFunction) { + BinLogEventType eventType = BinLogEventType.fromType(event.getType()); + if (Objects.equals(BinLogEventType.CREATE, eventType) || Objects.equals(BinLogEventType.ALTER, eventType)) { + if (log.isDebugEnabled()) { + log.debug("监听到不需要处理或者未知的binlog事件类型[{}],将忽略解析过程返回空列表,binlog事件:{}", eventType, JSON.toJSONString(event)); + } + return Collections.emptyList(); + } + + if (BinLogEventType.UNKNOWN != eventType && BinLogEventType.QUERY != eventType) { + if (Boolean.TRUE.equals(event.getIsDdl())) { + CanalBinLogResult entry = new CanalBinLogResult<>(); + entry.setOperationType(OperationType.DDL); + entry.setBinLogEventType(eventType); + entry.setDatabaseName(event.getDatabase()); + entry.setTableName(event.getTable()); + entry.setSql(event.getSql()); + return Collections.singletonList(entry); + } else { + Optional.ofNullable(event.getPkNames()).filter((x) -> { + return x.size() == 1; + }).orElseThrow(() -> { + return new IllegalArgumentException("DML类型binlog事件主键列数量不为1"); + }); + String primaryKeyName = (String)event.getPkNames().get(0); + List> entryList = new LinkedList<>(); + List> data = event.getData(); + List> old = event.getOld(); + int dataSize = null != data ? data.size() : 0; + int oldSize = null != old ? old.size() : 0; + if (dataSize > 0) { + for(int index = 0; index < dataSize; ++index) { + CanalBinLogResult entry = new CanalBinLogResult<>(); + entryList.add(entry); + entry.setSql(event.getSql()); + entry.setOperationType(OperationType.DML); + entry.setBinLogEventType(eventType); + entry.setTableName(event.getTable()); + entry.setDatabaseName(event.getDatabase()); + Map item = data.get(index); + entry.setAfterData(commonEntryFunction.apply(item)); + Map oldItem = null; + if (oldSize > 0 && index <= oldSize) { + oldItem = old.get(index); + entry.setBeforeData(commonEntryFunction.apply(oldItem)); + } + + entry.setPrimaryKey(primaryKeyFunction.apply(oldItem, item, primaryKeyName)); + } + } + + return entryList; + } + } else { + if (log.isDebugEnabled()) { + log.debug("监听到不需要处理或者未知的binlog事件类型[{}],将忽略解析过程返回空列表,binlog事件:{}", eventType, JSON.toJSONString(event)); + } + + return Collections.emptyList(); + } + } + + private mall4cloudCanalBinLogEventParser() { + } + + public static mall4cloudCanalBinLogEventParser of() { + return new mall4cloudCanalBinLogEventParser(); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinlogEventProcessorFactory.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinlogEventProcessorFactory.java new file mode 100644 index 00000000..7b106f07 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalBinlogEventProcessorFactory.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.search.canal; + +import cn.throwx.canal.gule.model.ModelTable; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import cn.throwx.canal.gule.support.processor.CanalBinlogEventProcessorFactory; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author FrozenWatermelon + */ +public class mall4cloudCanalBinlogEventProcessorFactory implements CanalBinlogEventProcessorFactory { + + private final ConcurrentMap>> cache = new ConcurrentHashMap<>(16); + + @Override + public void register(ModelTable modelTable, BaseCanalBinlogEventProcessor processor) { + synchronized(this.cache) { + this.cache.putIfAbsent(modelTable, new LinkedList<>()); + this.cache.get(modelTable).add(processor); + } + } + + @Override + public List> get(ModelTable modelTable) { + return this.cache.get(modelTable); + } + + private mall4cloudCanalBinlogEventProcessorFactory() { + } + + public static mall4cloudCanalBinlogEventProcessorFactory of() { + return new mall4cloudCanalBinlogEventProcessorFactory(); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalGlue.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalGlue.java new file mode 100644 index 00000000..f0d6ca46 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/canal/mall4cloudCanalGlue.java @@ -0,0 +1,41 @@ +package com.mall4j.cloud.search.canal; + +import cn.hutool.core.collection.CollectionUtil; +import cn.throwx.canal.gule.CanalGlue; +import cn.throwx.canal.gule.model.CanalBinLogEvent; +import cn.throwx.canal.gule.model.ModelTable; +import cn.throwx.canal.gule.support.adapter.SourceAdapterFacade; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import cn.throwx.canal.gule.support.processor.CanalBinlogEventProcessorFactory; + +import java.util.List; + +/** + * @author FrozenWatermelon + */ +public class mall4cloudCanalGlue implements CanalGlue { + + private final CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory; + + @Override + public void process(String content) { + CanalBinLogEvent event = (CanalBinLogEvent) SourceAdapterFacade.X.adapt(CanalBinLogEvent.class, content); + ModelTable modelTable = ModelTable.of(event.getDatabase(), event.getTable()); + List> baseCanalBinlogEventProcessors = this.canalBinlogEventProcessorFactory.get(modelTable); + if (CollectionUtil.isEmpty(baseCanalBinlogEventProcessors)) { + return; + } + baseCanalBinlogEventProcessors.forEach((processor) -> { + processor.process(event); + }); + } + + + private mall4cloudCanalGlue(CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory) { + this.canalBinlogEventProcessorFactory = canalBinlogEventProcessorFactory; + } + + public static mall4cloudCanalGlue of(CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory) { + return new mall4cloudCanalGlue(canalBinlogEventProcessorFactory); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/CanalGlueAutoConfiguration.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/CanalGlueAutoConfiguration.java new file mode 100644 index 00000000..11b38730 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/CanalGlueAutoConfiguration.java @@ -0,0 +1,95 @@ +package com.mall4j.cloud.search.config; + +import cn.throwx.canal.gule.CanalGlue; +import cn.throwx.canal.gule.support.parser.*; +import cn.throwx.canal.gule.support.parser.converter.CanalFieldConverterFactory; +import cn.throwx.canal.gule.support.parser.converter.InMemoryCanalFieldConverterFactory; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import cn.throwx.canal.gule.support.processor.CanalBinlogEventProcessorFactory; +import com.mall4j.cloud.search.canal.mall4cloudCanalBinLogEventParser; +import com.mall4j.cloud.search.canal.mall4cloudCanalBinlogEventProcessorFactory; +import com.mall4j.cloud.search.canal.mall4cloudCanalGlue; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import java.util.Map; + +/** + * @author throwable + * @version v1 + * @description + * @since 2020/9/20 17:05 + */ +@Configuration +public class CanalGlueAutoConfiguration implements SmartInitializingSingleton, BeanFactoryAware { + + private ConfigurableListableBeanFactory configurableListableBeanFactory; + + @Bean + @ConditionalOnMissingBean + public CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory() { + return mall4cloudCanalBinlogEventProcessorFactory.of(); + } + + @Bean + @ConditionalOnMissingBean + public ModelTableMetadataManager modelTableMetadataManager(CanalFieldConverterFactory canalFieldConverterFactory) { + return InMemoryModelTableMetadataManager.of(canalFieldConverterFactory); + } + + @Bean + @ConditionalOnMissingBean + public CanalFieldConverterFactory canalFieldConverterFactory() { + return InMemoryCanalFieldConverterFactory.of(); + } + + @Bean + @ConditionalOnMissingBean + public CanalBinLogEventParser canalBinLogEventParser() { + return mall4cloudCanalBinLogEventParser.of(); + } + + @Bean + @ConditionalOnMissingBean + public ParseResultInterceptorManager parseResultInterceptorManager(ModelTableMetadataManager modelTableMetadataManager) { + return InMemoryParseResultInterceptorManager.of(modelTableMetadataManager); + } + + @Bean + @Primary + public CanalGlue canalGlue(CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory) { + return mall4cloudCanalGlue.of(canalBinlogEventProcessorFactory); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.configurableListableBeanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public void afterSingletonsInstantiated() { + ParseResultInterceptorManager parseResultInterceptorManager + = configurableListableBeanFactory.getBean(ParseResultInterceptorManager.class); + ModelTableMetadataManager modelTableMetadataManager + = configurableListableBeanFactory.getBean(ModelTableMetadataManager.class); + CanalBinlogEventProcessorFactory canalBinlogEventProcessorFactory + = configurableListableBeanFactory.getBean(CanalBinlogEventProcessorFactory.class); + CanalBinLogEventParser canalBinLogEventParser + = configurableListableBeanFactory.getBean(CanalBinLogEventParser.class); + Map interceptors + = configurableListableBeanFactory.getBeansOfType(BaseParseResultInterceptor.class); + interceptors.forEach((k, interceptor) -> parseResultInterceptorManager.registerParseResultInterceptor(interceptor)); + Map processors + = configurableListableBeanFactory.getBeansOfType(BaseCanalBinlogEventProcessor.class); + processors.forEach((k, processor) -> processor.init(canalBinLogEventParser, modelTableMetadataManager, + canalBinlogEventProcessorFactory, parseResultInterceptorManager)); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/ElasticConfig.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/ElasticConfig.java new file mode 100644 index 00000000..930821b1 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/ElasticConfig.java @@ -0,0 +1,22 @@ +package com.mall4j.cloud.search.config; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author FrozenWatermelon + * @date 2020/9/24 + */ +@Configuration +public class ElasticConfig { + + @Bean + public RestHighLevelClient restHighLevelClient() { + return new RestHighLevelClient( + RestClient.builder( + new HttpHost("192.168.1.46", 9200, "http"))); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/SwaggerConfiguration.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/SwaggerConfiguration.java new file mode 100644 index 00000000..abd2ab27 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.search.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.search.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/DataType.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/DataType.java new file mode 100644 index 00000000..6e533d62 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/DataType.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.search.constant; + +/** + * es当中的index + * @author FrozenWatermelon + * @date 2020/11/12 + */ +public enum DataType { + + /** + * 全部 + */ + ALL(0), + + /** + * 销售中 + */ + SALE(1), + + /** + * 已售罄 + */ + SOLD_OUT(2), + + /** + * 已下架 + */ + DISABLE(3) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + DataType(Integer value) { + this.value = value; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsConstant.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsConstant.java new file mode 100644 index 00000000..a2a9cd01 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsConstant.java @@ -0,0 +1,74 @@ +package com.mall4j.cloud.search.constant; + +/** + * 配置缓存名字 + * + * @author lhd + * @date 2020/12/30 + */ +public interface EsConstant { + + /** + * 商品 + */ + String SHOP = "shop"; + String SPU_ID = "spuId"; + String SPU_NAME = "spuName"; + String SPU_STATUS = "spuStatus"; + String TAGS = "tags"; + String TAG_ID = TAGS + ".tagId"; + String SALE_NUM = "saleNum"; + String PRICE_FEE = "priceFee"; + String HAS_STOCK = "hasStock"; + String SELLING_POINT = "sellingPoint"; + + /** + * 店铺 + */ + String SHOP_ID = "shopId"; + String SHOP_NAME = "shopName"; + String SHOP_TYPE = "shopType"; + String SHOP_COUPON = "shop_coupon"; + + /** + * 属性 + */ + String ATTRS = "attrs"; + String ATTR_IDS = "attrIds"; + String ATTR_VALUE_IDS = "attrValueIds"; + String ATTR_ATTR_VALUE_ID = ATTRS + ".attrValueId"; + String ATTR_ATTR_NAME = ATTRS + ".attrName"; + String ATTR_ATTR_VALUE_NAME = ATTRS + ".attrValueName"; + String ATTR_ATTR_ID = ATTRS + ".attrId"; + + /** + * 品牌 + */ + String BRANDS = "brands"; + String BRAND_ID = "brandId"; + String BRAND_IMG = "brandImg"; + String BRAND_NAME = "brandName"; + + /** + * 分类 + */ + String MAIN_IMG_URL = "mainImgUrl"; + String CATEGORIES = "categories"; + String CATEGORY_ID = "categoryId"; + String CATEGORY_NAME = "categoryName"; + String SHOP_CATEGORIES = "shopCategories"; + String SHOP_CATEGORY_ID = "secondaryCategoryId"; + String SHOP_CATEGORY_NAME = "secondaryCategoryName"; + String PRIMARY_CATEGORY_ID = "primaryCategoryId"; + String SHOP_SECONDARY_CATEGORY_ID = "shopSecondaryCategoryId"; + String SHOP_PRIMARY_CATEGORY_ID = "shopPrimaryCategoryId"; + + String TOP_HITS_DATA = "top_hits_data"; + String CONNECTION_SYMBOLS = ","; + + /** + * 指定返回字段 + */ + String[] FETCH_SOURCE = {"spuId","spuName","sellingPoint","shopId","priceFee","marketPriceFee","hasStock","saleNum","mainImgUrl","stock","seq","spuStatus"}; + +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsIndexEnum.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsIndexEnum.java new file mode 100644 index 00000000..a8db8a91 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsIndexEnum.java @@ -0,0 +1,31 @@ +package com.mall4j.cloud.search.constant; + +/** + * es当中的index + * @author FrozenWatermelon + * @date 2020/11/12 + */ +public enum EsIndexEnum { + + /** + * 商品 + */ + PRODUCT("product"), + + /** + * 订单 + */ + ORDER("order"), + + ; + + private final String value; + + public String value() { + return value; + } + + EsIndexEnum(String value) { + this.value = value; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsProductSortEnum.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsProductSortEnum.java new file mode 100644 index 00000000..3208a195 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/EsProductSortEnum.java @@ -0,0 +1,79 @@ +package com.mall4j.cloud.search.constant; + +import java.util.Objects; + +/** + * es当中的商品排序 + * @author FrozenWatermelon + * @date 2020/11/17 + */ +public enum EsProductSortEnum { + + /** + * 新品(创建时间) + */ + CREATE_TIME(1, "createTime", Boolean.TRUE), + + /** + * 销量倒序 + */ + SALE_NUM_DESC(2, "saleNum", Boolean.FALSE), + + /** + * 销量正序 + */ + SALE_NUM_ASC(3, "saleNum", Boolean.TRUE), + + /** + * 商品价格倒序 + */ + PRICE_DESC(4, "priceFee", Boolean.FALSE), + + /** + * 商品价格正序 + */ + PRICE_ASC(5, "priceFee", Boolean.TRUE), + + /** + * 分组排序 + */ + SPU_TAG(6, "tags.seq", null) + ; + + private final Integer value; + + private final String sort; + + private final Boolean order; + + public Integer value() { + return value; + } + + EsProductSortEnum(Integer value,String sort,Boolean order) { + this.value = value; + this.sort = sort; + this.order = order; + } + + public String sort() { + return sort; + } + + public Boolean order() { + return order; + } + + public static Boolean isAsc(EsProductSortEnum esProductSortEnum) { + if (Objects.equals(esProductSortEnum.order(), Boolean.TRUE)) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + public static Boolean isDesc(EsProductSortEnum esProductSortEnum) { + if (Objects.equals(esProductSortEnum.order(), Boolean.FALSE)) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/SearchTypeEnum.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/SearchTypeEnum.java new file mode 100644 index 00000000..ac5c28a9 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/constant/SearchTypeEnum.java @@ -0,0 +1,35 @@ +package com.mall4j.cloud.search.constant; + +/** + * 搜索平台的类型 + * @author YXF + * @date 2021/03/16 + */ +public enum SearchTypeEnum { + + /** + * 用户端搜索 + */ + APP(1), + + /** + * 商家端、平台端搜索 + */ + MULTISHOP(2), + + /** + * 商家端、平台端搜索 + */ + PLATFORM(3) + ; + + private final Integer value; + + public Integer value() { + return value; + } + + SearchTypeEnum(Integer value) { + this.value = value; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/app/ProductSearchController.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/app/ProductSearchController.java new file mode 100644 index 00000000..1cc8fc3c --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/app/ProductSearchController.java @@ -0,0 +1,82 @@ +package com.mall4j.cloud.search.controller.app; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.multishop.bo.EsShopDetailBO; +import com.mall4j.cloud.api.multishop.feign.ShopDetailFeignClient; +import com.mall4j.cloud.api.vo.search.ShopInfoSearchVO; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.search.manager.ProductSearchManager; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.ProductSearchVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * 商品搜索 + * @author FrozenWatermelon + * @date 2020/11/16 + */ +@RestController("appSearchSpuController") +@RequestMapping("/ua/search") +@Api(tags = "app-spu搜索接口") +public class ProductSearchController { + + @Autowired + private ProductSearchManager productSearchManager; + + @Autowired + private ShopDetailFeignClient shopDetailFeignClient; + + @GetMapping("/page") + @ApiOperation(value = "商品信息列表-包含spu、品牌、分类、属性和店铺信息", notes = "spu列表-包含spu、品牌、分类、属性和店铺信息") + public ServerResponseEntity> page(@Valid EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + productSearchDTO.setSpuStatus(StatusEnum.ENABLE.value()); + EsPageVO searchPage = productSearchManager.page(pageDTO, productSearchDTO); + loadShopData(searchPage.getList()); + return ServerResponseEntity.success(searchPage); + } + + @GetMapping("/simple_page") + @ApiOperation(value = "商品信息列表-包含spu信息", notes = "商品信息列表-包含spu信息") + public ServerResponseEntity> simplePage(@Valid EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + productSearchDTO.setSpuStatus(StatusEnum.ENABLE.value()); + EsPageVO searchPage = productSearchManager.simplePage(pageDTO, productSearchDTO); + loadShopData(searchPage.getList()); + return ServerResponseEntity.success(searchPage); + } + + /** + * 获取店铺数据 + * @param list + */ + private void loadShopData(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (ProductSearchVO productSearchVO : list) { + if (Objects.isNull(productSearchVO.getShopInfo()) || Objects.isNull(productSearchVO.getShopInfo().getShopId())) { + continue; + } + ServerResponseEntity shopDataResponse = shopDetailFeignClient.shopExtensionData(productSearchVO.getShopInfo().getShopId()); + if (Objects.equals(shopDataResponse.getCode(), ResponseEnum.OK.value())) { + EsShopDetailBO esShopDetailBO = shopDataResponse.getData(); + ShopInfoSearchVO shopInfo = productSearchVO.getShopInfo(); + shopInfo.setShopLogo(esShopDetailBO.getShopLogo()); + shopInfo.setShopName(esShopDetailBO.getShopName()); + shopInfo.setType(esShopDetailBO.getType()); + } + } + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/multishop/ProductSearchController.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/multishop/ProductSearchController.java new file mode 100644 index 00000000..c6b12a2e --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/multishop/ProductSearchController.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.search.controller.multishop; + +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.search.constant.SearchTypeEnum; +import com.mall4j.cloud.search.manager.ProductSearchManager; +import com.mall4j.cloud.search.vo.SpuAdminVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @Author lth + * @Date 2021/6/21 10:36 + */ +@RestController("multishopSearchSpuController") +@RequestMapping("/m/search") +@Api(tags = "multishop-spu管理列表接口") +public class ProductSearchController { + + @Autowired + private ProductSearchManager productSearchManager; + + @GetMapping("/page") + @ApiOperation(value = "商品信息列表", notes = "商品信息列表") + public ServerResponseEntity> page(@Valid EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + Long shopId = AuthUserContext.get().getTenantId(); + productSearchDTO.setSearchType(SearchTypeEnum.MULTISHOP.value()); + productSearchDTO.setShopId(shopId); + EsPageVO searchPage = productSearchManager.adminPage(pageDTO, productSearchDTO); + return ServerResponseEntity.success(searchPage); + } + +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/platform/ProductSearchController.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/platform/ProductSearchController.java new file mode 100644 index 00000000..e1a29661 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/controller/platform/ProductSearchController.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.search.controller.platform; + +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.constant.SearchTypeEnum; +import com.mall4j.cloud.search.manager.ProductSearchManager; +import com.mall4j.cloud.search.vo.SpuAdminVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @Author lth + * @Date 2021/6/21 13:48 + */ +@RestController("platformSearchSpuController") +@RequestMapping("/p/search") +@Api(tags = "platform-spu列表接口") +public class ProductSearchController { + + @Autowired + private ProductSearchManager productSearchManager; + + @GetMapping("/page") + @ApiOperation(value = "商品管理信息列表(平台端)", notes = "商品管理信息列表(平台端)") + public ServerResponseEntity> adminPage(@Valid EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + productSearchDTO.setSearchType(SearchTypeEnum.PLATFORM.value()); + EsPageVO searchPage = productSearchManager.adminPage(pageDTO, productSearchDTO); + return ServerResponseEntity.success(searchPage); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchOrderFeignController.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchOrderFeignController.java new file mode 100644 index 00000000..9d09a890 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchOrderFeignController.java @@ -0,0 +1,33 @@ +package com.mall4j.cloud.search.feign; + + +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.feign.SearchOrderFeignClient; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.EsOrderVO; +import com.mall4j.cloud.common.dto.OrderSearchDTO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.manager.OrderSearchManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * 商品搜索feign连接 + * @author YXF + * @date 2020/12/07 + */ +@RestController +public class SearchOrderFeignController implements SearchOrderFeignClient { + + @Autowired + private OrderSearchManager orderSearchManager; + + + @Override + public ServerResponseEntity> getOrderPage(OrderSearchDTO orderSearch) { + EsPageDTO pageDTO = new EsPageDTO(); + pageDTO.setPageNum(orderSearch.getPageNum()); + pageDTO.setPageSize(orderSearch.getPageSize()); + return ServerResponseEntity.success(orderSearchManager.pageSearchResult(pageDTO, orderSearch)); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchSpuFeignController.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchSpuFeignController.java new file mode 100644 index 00000000..dd7ce063 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/feign/SearchSpuFeignController.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.search.feign; + + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.api.feign.SearchSpuFeignClient; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.ProductSearchVO; +import com.mall4j.cloud.api.vo.search.SpuSearchVO; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.manager.ProductSearchManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * 商品搜索feign连接 + * @author YXF + * @date 2020/12/07 + */ +@RestController +public class SearchSpuFeignController implements SearchSpuFeignClient { + + @Autowired + private ProductSearchManager productSearchManager; + + @Override + public ServerResponseEntity> search(EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + return ServerResponseEntity.success(productSearchManager.simplePage(pageDTO, productSearchDTO)); + } + + @Override + public ServerResponseEntity> getSpusBySpuIds(List spuIds){ + if (CollUtil.isEmpty(spuIds)) { + return ServerResponseEntity.success(new ArrayList<>()); + } + ProductSearchDTO productSearchDTO = new ProductSearchDTO(); + productSearchDTO.setSpuIds(spuIds); + List list = productSearchManager.list(productSearchDTO); + return ServerResponseEntity.success(list); + } + + @Override + public ServerResponseEntity> spuPage(Integer pageNum, Integer pageSize, Long shopId) { + EsPageDTO pageDTO = new EsPageDTO(); + pageDTO.setPageNum(pageNum); + pageDTO.setPageSize(pageSize); + ProductSearchDTO productSearchDTO = new ProductSearchDTO(); + // 平台id则搜索整个平台的商品 + if (!Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) { + productSearchDTO.setShopId(shopId); + } + EsPageVO page = productSearchManager.page(pageDTO, productSearchDTO); + return ServerResponseEntity.success(page); + } + + @Override + public ServerResponseEntity> limitSizeListByShopIds(List shopIds, Integer size) { + if (CollUtil.isEmpty(shopIds)) { + return ServerResponseEntity.success(new ArrayList<>()); + } + List list = productSearchManager.limitSizeListByShopIds(shopIds, size); + return ServerResponseEntity.success(list); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/BrandCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/BrandCanalListener.java new file mode 100644 index 00000000..2485ac82 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/BrandCanalListener.java @@ -0,0 +1,62 @@ +package com.mall4j.cloud.search.listener; + +import cn.hutool.core.util.StrUtil; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.feign.ProductFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.bo.BrandBO; +import com.mall4j.cloud.search.manager.ProductUpdateManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +/** + * @author YXF + * @date 2021/01/21 + */ +@Component +public class BrandCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(BrandCanalListener.class); + + @Autowired + private ProductUpdateManager productUpdateManager; + @Autowired + private ProductFeignClient productFeignClient; + + /** + * 新增品牌 + */ + @Override + protected void processInsertInternal(CanalBinLogResult brandResult) { + + } + + /** + * 更新品牌 + * @param result + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + BrandBO beforeData = result.getBeforeData(); + if (Objects.isNull(beforeData.getName()) && StrUtil.isBlank(beforeData.getImgUrl())) { + return; + } + BrandBO afterData = result.getAfterData(); + EsProductBO esProductBO = new EsProductBO(); + if (StrUtil.isNotBlank(beforeData.getName())) { + esProductBO.setBrandName(afterData.getName()); + } + if (Objects.nonNull(beforeData.getImgUrl())) { + esProductBO.setBrandImg(afterData.getImgUrl()); + } + ServerResponseEntity> responseData = productFeignClient.getSpuIdsByBrandId(afterData.getBrandId()); + productUpdateManager.esUpdateSpuBySpuIds(responseData.getData(), esProductBO); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CanalListener.java new file mode 100644 index 00000000..13cc94d7 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CanalListener.java @@ -0,0 +1,25 @@ +package com.mall4j.cloud.search.listener; + +import cn.throwx.canal.gule.CanalGlue; +import com.mall4j.cloud.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author FrozenWatermelon + * @date 2021/02/03 + */ +@Component +@RocketMQMessageListener(topic = RocketMqConstant.CANAL_TOPIC,consumerGroup = RocketMqConstant.CANAL_TOPIC) +public class CanalListener implements RocketMQListener { + + @Autowired + private CanalGlue canalGlue; + + @Override + public void onMessage(String message) { + canalGlue.process(message); + } +} \ No newline at end of file diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CategoryCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CategoryCanalListener.java new file mode 100644 index 00000000..6f40c3ff --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/CategoryCanalListener.java @@ -0,0 +1,125 @@ +package com.mall4j.cloud.search.listener; + +import cn.hutool.core.util.StrUtil; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.constant.CategoryLevel; +import com.mall4j.cloud.api.product.feign.CategoryFeignClient; +import com.mall4j.cloud.api.product.feign.ProductFeignClient; +import com.mall4j.cloud.common.constant.Constant; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.bo.CategoryBO; +import com.mall4j.cloud.search.manager.ProductUpdateManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/11/13 + */ +@Component +public class CategoryCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(CategoryCanalListener.class); + + @Autowired + private CategoryFeignClient categoryFeignClient; + @Autowired + private ProductUpdateManager productUpdateManager; + @Autowired + private ProductFeignClient productFeignClient; + + /** + * 插入商品,此时插入es + */ + @Override + protected void processInsertInternal(CanalBinLogResult categoryBo) { + + } + + /** + * 更新分类,删除商品索引,再重新构建一个 + * @param result + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + CategoryBO beforeData = result.getBeforeData(); + if (Objects.isNull(beforeData.getName()) && Objects.isNull(beforeData.getStatus())) { + return; + } + CategoryBO afterData = result.getAfterData(); + EsProductBO esProductBO = new EsProductBO(); + if (StrUtil.isNotBlank(beforeData.getName())) { + insertCategoryName(esProductBO, afterData); + } + // 更新分类列表下商品的状态 + if (Objects.nonNull(beforeData.getStatus())) { + esProductBO.setSpuStatus(beforeData.getStatus()); + } + List spuIds = getSpuIdsByCategoryId(afterData); + productUpdateManager.esUpdateSpuBySpuIds(spuIds, esProductBO); + } + + /** + * 插入需要修改的分类名称 + * @param esProductBO + * @param afterData + */ + private void insertCategoryName(EsProductBO esProductBO, CategoryBO afterData) { + // 平台分类 + if (Objects.equals(Constant.PLATFORM_SHOP_ID, afterData.getShopId())) { + if (afterData.getLevel().equals(CategoryLevel.First.value())) { + esProductBO.setPrimaryCategoryName(afterData.getName()); + } else if (afterData.getLevel().equals(CategoryLevel.SECOND.value())) { + esProductBO.setSecondaryCategoryName(afterData.getName()); + } else { + esProductBO.setCategoryName(afterData.getName()); + } + } + // 商家分类 + else { + if (afterData.getLevel().equals(CategoryLevel.First.value())) { + esProductBO.setShopPrimaryCategoryName(afterData.getName()); + } else if (afterData.getLevel().equals(CategoryLevel.SECOND.value())) { + esProductBO.setShopSecondaryCategoryName(afterData.getName()); + } + } + + } + + + /** + * 根据分类信息,获取分类下的商品Id列表 + * @param category + * @return + */ + private List getSpuIdsByCategoryId(CategoryBO category) { + List spuIds = new ArrayList<>(); + ServerResponseEntity> spuIdResponse = null; + List categoryIds = new ArrayList<>(); + Boolean isSearch = (category.getShopId().equals(Constant.PLATFORM_SHOP_ID) && !category.getLevel().equals(CategoryLevel.THIRD.value())) && + (!category.getShopId().equals(Constant.PLATFORM_SHOP_ID) && category.getLevel().equals(CategoryLevel.First.value())); + // 平台分类 + if (isSearch) { + ServerResponseEntity> categoryResponse = categoryFeignClient.listCategoryId(category.getCategoryId()); + categoryIds.addAll(categoryResponse.getData()); + } else { + categoryIds.add(category.getCategoryId()); + } + + if (Objects.equals(category.getShopId(), Constant.PLATFORM_SHOP_ID)) { + spuIdResponse = productFeignClient.getSpuIdsByCategoryIds(categoryIds); + } else { + spuIdResponse = productFeignClient.getSpuIdsByShopCategoryIds(categoryIds); + } + spuIds.addAll(spuIdResponse.getData()); + return spuIds; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/OrderCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/OrderCanalListener.java new file mode 100644 index 00000000..f4e2508c --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/OrderCanalListener.java @@ -0,0 +1,90 @@ +package com.mall4j.cloud.search.listener; + +import cn.throwx.canal.gule.model.CanalBinLogEvent; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import cn.throwx.canal.gule.support.processor.ExceptionHandler; +import com.mall4j.cloud.api.order.bo.EsOrderBO; +import com.mall4j.cloud.api.order.feign.OrderFeignClient; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.bo.OrderBO; +import com.mall4j.cloud.search.constant.EsIndexEnum; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * 订单的监听 + * @author FrozenWatermelon + * @date 2021/02/03 + */ +@Component +public class OrderCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(OrderCanalListener.class); + + @Autowired + private OrderFeignClient orderFeignClient; + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 插入订单,此时插入es + */ + @Override + protected void processInsertInternal(CanalBinLogResult result) { + Long orderId = result.getPrimaryKey(); + + ServerResponseEntity esOrderResponse = orderFeignClient.getEsOrder(orderId); + IndexRequest request = new IndexRequest(EsIndexEnum.ORDER.value()); + request.id(String.valueOf(orderId)); + request.source(Json.toJsonString(esOrderResponse.getData()), XContentType.JSON); + try { + IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT); + log.info(indexResponse.toString()); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.toString()); + throw new mall4cloudException("保存es信息异常", e); + } + } + + /** + * 更新订单,删除订单索引,再重新构建一个 + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + Long orderId = result.getPrimaryKey(); + ServerResponseEntity esOrderResponse = orderFeignClient.getEsOrder(orderId); + UpdateRequest request = new UpdateRequest(EsIndexEnum.ORDER.value(), String.valueOf(orderId)); + request.doc(Json.toJsonString(esOrderResponse.getData()), XContentType.JSON); + request.docAsUpsert(true); + try { + UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT); + log.info(updateResponse.toString()); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("更新订单es信息异常",e); + } + } + + @Override + protected ExceptionHandler exceptionHandler() { + return (CanalBinLogEvent event, Throwable throwable) -> { + throw new mall4cloudException("创建索引异常",throwable); + }; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/ShopDetailCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/ShopDetailCanalListener.java new file mode 100644 index 00000000..2fadb406 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/ShopDetailCanalListener.java @@ -0,0 +1,66 @@ +package com.mall4j.cloud.search.listener; + +import cn.hutool.core.util.StrUtil; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.feign.ProductFeignClient; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.search.bo.ShopDetailBO; +import com.mall4j.cloud.search.manager.ProductUpdateManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +/** + * @author YXF + * @date 2021/01/21 + */ +@Component +public class ShopDetailCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(ShopDetailCanalListener.class); + + @Autowired + private ProductUpdateManager productUpdateManager; + @Autowired + private ProductFeignClient productFeignClient; + + /** + * 新增店铺 + */ + @Override + protected void processInsertInternal(CanalBinLogResult shopDetailResult) { + + } + + /** + * 更新店铺 + * @param result + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + ShopDetailBO beforeData = result.getBeforeData(); + if (Objects.isNull(beforeData.getShopName()) && StrUtil.isBlank(beforeData.getShopLogo()) && !Objects.equals(beforeData.getShopId(), StatusEnum.ENABLE.value())) { + return; + } + ShopDetailBO afterData = result.getAfterData(); + EsProductBO esProductBO = new EsProductBO(); + if (StrUtil.isNotBlank(beforeData.getShopName())) { + esProductBO.setShopName(afterData.getShopName()); + } + if (Objects.nonNull(beforeData.getShopLogo())) { + esProductBO.setShopImg(afterData.getShopLogo()); + } + if (Objects.nonNull(beforeData.getShopStatus()) && Objects.equals(beforeData.getShopId(), StatusEnum.ENABLE.value())) { + esProductBO.setSpuStatus(StatusEnum.DISABLE.value()); + } + ServerResponseEntity> responseData = productFeignClient.getSpuIdsByShopId(afterData.getShopId()); + productUpdateManager.esUpdateSpuBySpuIds(responseData.getData(), esProductBO); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuCanalListener.java new file mode 100644 index 00000000..3896e753 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuCanalListener.java @@ -0,0 +1,93 @@ +package com.mall4j.cloud.search.listener; + +import cn.throwx.canal.gule.model.CanalBinLogEvent; +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import cn.throwx.canal.gule.support.processor.ExceptionHandler; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.api.product.feign.ProductFeignClient; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.bo.SpuBO; +import com.mall4j.cloud.search.constant.EsIndexEnum; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author FrozenWatermelon + * @date 2020/11/13 + */ +@Component +public class SpuCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(SpuCanalListener.class); + + @Autowired + private ProductFeignClient productFeignClient; + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 插入商品,此时插入es + */ + @Override + protected void processInsertInternal(CanalBinLogResult result) { + Long spuId = result.getPrimaryKey(); + ServerResponseEntity esProductBO = productFeignClient.loadEsProductBO(spuId); + if (!esProductBO.isSuccess()) { + throw new mall4cloudException("创建索引异常"); + } + + IndexRequest request = new IndexRequest(EsIndexEnum.PRODUCT.value()); + request.id(String.valueOf(spuId)); + request.source(Json.toJsonString(esProductBO.getData()), XContentType.JSON); + try { + IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT); + log.info(indexResponse.toString()); + + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("保存es信息异常", e); + } + } + + /** + * 更新商品,删除商品索引,再重新构建一个 + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + Long spuId = result.getPrimaryKey(); + ServerResponseEntity esProductBO = productFeignClient.loadEsProductBO(spuId); + String source = Json.toJsonString(esProductBO.getData()); + UpdateRequest request = new UpdateRequest(EsIndexEnum.PRODUCT.value(), String.valueOf(spuId)); + request.doc(source, XContentType.JSON); + request.docAsUpsert(true); + try { + UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT); + log.info(updateResponse.toString()); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("删除es信息异常",e); + } + } + + @Override + protected ExceptionHandler exceptionHandler() { + return (CanalBinLogEvent event, Throwable throwable) -> { + throw new mall4cloudException("创建索引异常",throwable); + }; + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuExtensionCanalListener.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuExtensionCanalListener.java new file mode 100644 index 00000000..eb555acd --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/listener/SpuExtensionCanalListener.java @@ -0,0 +1,77 @@ +package com.mall4j.cloud.search.listener; + + +import cn.throwx.canal.gule.model.CanalBinLogResult; +import cn.throwx.canal.gule.support.processor.BaseCanalBinlogEventProcessor; +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.cache.util.CacheManagerUtil; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.bo.SpuExtensionBO; +import com.mall4j.cloud.search.constant.EsIndexEnum; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author FrozenWatermelon + * @date 2020/11/13 + */ +@Component +public class SpuExtensionCanalListener extends BaseCanalBinlogEventProcessor { + + private static final Logger log = LoggerFactory.getLogger(SpuExtensionCanalListener.class); + + @Autowired + private CacheManagerUtil cacheManagerUtil; + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 插入商品,此时插入es + */ + @Override + protected void processInsertInternal(CanalBinLogResult result) { + System.out.println(); + } + + /** + * 更新商品,删除商品索引,再重新构建一个 + */ + @Override + protected void processUpdateInternal(CanalBinLogResult result) { + // 更新之后的数据 + SpuExtensionBO afterData = result.getAfterData(); + + // 清除缓存 + cacheManagerUtil.evictCache(CacheNames.SPU_EXTENSION_KEY, afterData.getSpuId().toString()); + + UpdateRequest request = new UpdateRequest(EsIndexEnum.PRODUCT.value(), String.valueOf(afterData.getSpuId())); + + EsProductBO esProductBO = new EsProductBO(); + // 可售库存 + esProductBO.setSpuId(afterData.getSpuId()); + esProductBO.setStock(afterData.getStock()); + esProductBO.setHasStock(afterData.getStock() != 0); + esProductBO.setSaleNum(afterData.getSaleNum()); + + request.doc(Json.toJsonString(esProductBO), XContentType.JSON); + try { + UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT); + log.info(updateResponse.toString()); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("更新es信息异常",e); + } + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/OrderSearchManager.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/OrderSearchManager.java new file mode 100644 index 00000000..8da9bc4d --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/OrderSearchManager.java @@ -0,0 +1,256 @@ +package com.mall4j.cloud.search.manager; + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.vo.EsPageVO; +import com.mall4j.cloud.api.vo.search.EsOrderVO; +import com.mall4j.cloud.common.dto.OrderSearchDTO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.constant.EsIndexEnum; +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.NestedQueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.RangeQueryBuilder; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +@Component +public class OrderSearchManager { + + private static final Logger log = LoggerFactory.getLogger(OrderSearchManager.class); + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 通过搜索信息分页搜索es数据的信息 + * + * @param pageDTO 分页数据 + * @param orderSearchDTO 订单搜索条件 + * @return 搜索结果 + */ + public EsPageVO pageSearchResult(EsPageDTO pageDTO, OrderSearchDTO orderSearchDTO) { + //1、动态构建出查询需要的DSL语句 + EsPageVO result; + + //1、准备检索请求 + SearchRequest searchRequest = buildSearchRequest(pageDTO, orderSearchDTO); + + try { + //2、执行检索请求 + SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + + log.debug("搜索返回结果:" + response.toString()); + + //3、分析响应数据,封装成我们需要的格式 + result = buildSearchResult(pageDTO, response); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("搜索服务出了点小差,请稍后再试", e); + } + return result; + } + + /** + * 构建结果数据 + */ + private EsPageVO buildSearchResult(EsPageDTO pageDTO, SearchResponse response) { + EsPageVO esPageVO = new EsPageVO<>(); + + //1、返回的所有查询到的商品 + SearchHits hits = response.getHits(); + List productSearchs = getEsOrderBOList(response); + esPageVO.setList(productSearchs); + + + //===============分页信息====================// + //总记录数 + long total = hits.getTotalHits().value; + esPageVO.setTotal(total); + // 总页码 + int totalPages = (int) total % pageDTO.getPageSize() == 0 ? + (int) total / pageDTO.getPageSize() : ((int) total / pageDTO.getPageSize() + 1); + esPageVO.setPages(totalPages); + return esPageVO; + } + + private List getEsOrderBOList(SearchResponse response) { + + return getOrderListByResponse(response.getHits().getHits()); + } + + /** + * 从es返回的数据中获取spu列表 + * + * @param hits es返回的数据 + * @return + */ + private List getOrderListByResponse(SearchHit[] hits) { + List esOrders = new ArrayList<>(); + for (SearchHit hit : hits) { + EsOrderVO esOrder = Json.parseObject(hit.getSourceAsString(), EsOrderVO.class); + esOrders.add(esOrder); + } + return esOrders; + } + + + /** + * 准备检索请求 + * + * @param pageDTO 分页参数 + * @param param 搜索参数 + * @return + */ + private SearchRequest buildSearchRequest(EsPageDTO pageDTO, OrderSearchDTO param) { + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + + // 构建bool-query + BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); + + // 过滤 + filterQueryIfNecessary(param, boolQueryBuilder); + + // 关键字搜索 + keywordSearch(param, boolQueryBuilder); + + // 排序 + sort(searchSourceBuilder, boolQueryBuilder); + + //分页 + if (Objects.nonNull(pageDTO)) { + searchSourceBuilder.from((pageDTO.getPageNum() - 1) * pageDTO.getPageSize()); + searchSourceBuilder.size(pageDTO.getPageSize()); + } + + log.debug("构建的DSL语句 {}", searchSourceBuilder.toString()); + + return new SearchRequest(new String[]{EsIndexEnum.ORDER.value()}, searchSourceBuilder); + } + + + /** + * 关键字搜索 + */ + private void keywordSearch(OrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) { + + // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可 + + + BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery(); + + // 订单id + if (param.getOrderId() != null) { + keywordShouldQuery.should(QueryBuilders.matchQuery("orderId", param.getOrderId())); + } + + // 店铺名称 + if (StrUtil.isNotBlank(param.getShopName())) { + keywordShouldQuery.should(QueryBuilders.matchQuery("shopName", param.getShopName())); + } + + if (StrUtil.isNotBlank(param.getSpuName())) { + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + // 订单项商品名称搜索 + if (StrUtil.isNotBlank(param.getSpuName())) { + boolQuery.must(QueryBuilders.matchQuery("orderItems.spuName", param.getSpuName())); + } + NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("orderItems", boolQuery, ScoreMode.None); + keywordShouldQuery.should(nestedQueryBuilder); + } + + + //收货人姓名 + if (StrUtil.isNotBlank(param.getConsignee())) { + keywordShouldQuery.should(QueryBuilders.matchQuery("consignee", param.getConsignee())); + } + + //收货人手机号 + if (StrUtil.isNotBlank(param.getMobile())) { + keywordShouldQuery.should(QueryBuilders.matchQuery("mobile", param.getMobile())); + } + + + boolQueryBuilder.must(keywordShouldQuery); + } + + /** + * 进行排序 + */ + private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) { + searchSourceBuilder.sort("createTime", SortOrder.DESC); + searchSourceBuilder.query(boolQueryBuilder); + } + + /** + * 过滤查询条件,如果有必要的话 + * + * @param param 查询条件 + * @param boolQueryBuilder 组合进boolQueryBuilder + */ + private void filterQueryIfNecessary(OrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) { + // 店铺id + if (Objects.nonNull(param.getShopId())) { + boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId())); + } + + // 用户id + if (Objects.nonNull(param.getUserId())) { + boolQueryBuilder.filter(QueryBuilders.termQuery("userId", param.getUserId())); + } + + // 订单状态 参考OrderStatus + if (Objects.nonNull(param.getStatus()) && !Objects.equals(param.getStatus(), 0)) { + boolQueryBuilder.filter(QueryBuilders.termQuery("status", param.getStatus())); + } + + // 是否已经支付,1:已经支付过,0:,没有支付过 + if (Objects.nonNull(param.getIsPayed())) { + boolQueryBuilder.filter(QueryBuilders.termQuery("isPayed", param.getIsPayed())); + } + + // 开始时间 - 结束时间 + if (param.getStartTime() != null || param.getEndTime() != null) { + // 销售价 + String createTime = "createTime"; + RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime); + if (param.getStartTime() != null) { + rangeQueryBuilder.gte(param.getStartTime()); + } + if (param.getEndTime() != null) { + rangeQueryBuilder.lte(param.getEndTime()); + } + boolQueryBuilder.filter(rangeQueryBuilder); + } + + // 物流类型 3:无需快递 + if (BooleanUtil.isTrue(param.getDeliveryType())) { + boolQueryBuilder.filter(QueryBuilders.termQuery("deliveryType", param.getDeliveryType())); + } + + } + +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductSearchManager.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductSearchManager.java new file mode 100644 index 00000000..fba38e1c --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductSearchManager.java @@ -0,0 +1,692 @@ +package com.mall4j.cloud.search.manager; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.vo.*; +import com.mall4j.cloud.api.vo.search.*; +import com.mall4j.cloud.common.constant.StatusEnum; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.util.BooleanUtil; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.constant.*; +import com.mall4j.cloud.api.dto.EsPageDTO; +import com.mall4j.cloud.api.dto.ProductSearchDTO; +import com.mall4j.cloud.search.vo.SpuAdminVO; +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.lucene.search.function.CombineFunction; +import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction; +import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; +import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.functionscore.FieldValueFactorFunctionBuilder; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; +import org.elasticsearch.search.aggregations.bucket.terms.*; +import org.elasticsearch.search.aggregations.metrics.ParsedTopHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +@Component +public class ProductSearchManager { + + private static final Logger log = LoggerFactory.getLogger(ProductSearchManager.class); + + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 通过搜索信息分页搜索es数据并聚合返回的信息 + * @param pageDTO 分页数据 + * @param productSearchDTO 商品搜索条件 + * @return 搜索结果 + */ + public EsPageVO page(EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + productSearchDTO.setSpuStatus(StatusEnum.ENABLE.value()); + productSearchDTO.setSearchType(SearchTypeEnum.APP.value()); + SearchResponse response = pageSearchResult(pageDTO, productSearchDTO, Boolean.TRUE); + return buildSearchResult(pageDTO,response); + } + + /** + * 通过搜索信息分页搜索es数据的信息 + * @param pageDTO 分页数据 + * @param productSearchDTO 商品搜索条件 + * @return 搜索结果 + */ + public EsPageVO simplePage(EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + productSearchDTO.setSpuStatus(StatusEnum.ENABLE.value()); + productSearchDTO.setSearchType(SearchTypeEnum.APP.value()); + SearchResponse response = pageSearchResult(pageDTO, productSearchDTO, Boolean.FALSE); + return buildSearchResult(pageDTO,response); + } + + public List list(ProductSearchDTO productSearchDTO) { + //1、准备检索请求 + SearchRequest searchRequest = buildSearchRequest(null, productSearchDTO, Boolean.TRUE); + List spuList = null; + try { + //2、执行检索请求 + SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + + log.debug("搜索返回结果:" + response.toString()); + + //1、返回的所有查询到的商品 + spuList = getSpuListByResponse(response.getHits().getHits()); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("搜索服务出了点小差,请稍后再试", e); + } + return spuList; + } + + /** + * 通过搜索信息分页搜索es数据的信息 + * @param pageDTO 分页数据 + * @param productSearchDTO 商品搜索条件 + * @param isAgg true:聚合搜索 false:非聚合搜索 null:非聚合搜索 + * @return 搜索结果 + */ + private SearchResponse pageSearchResult(EsPageDTO pageDTO, ProductSearchDTO productSearchDTO, Boolean isAgg) { + //1、准备检索请求 + SearchRequest searchRequest = buildSearchRequest(pageDTO, productSearchDTO, isAgg); + SearchResponse response = null; + + try { + //2、执行检索请求 + response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + + log.debug("搜索返回结果:" + response.toString()); + } catch (IOException e) { + log.error(e.toString()); + throw new mall4cloudException("搜索服务出了点小差,请稍后再试", e); + } + return response; + } + + /** + * 构建结果数据 + */ + private EsPageVO buildSearchResult(EsPageDTO pageDTO, SearchResponse response) { + EsPageVO esPageVO = new EsPageVO<>(); + + //1、返回的所有查询到的商品 + SearchHits hits = response.getHits(); + List productSearchs = getProductSearchVOList(response); + esPageVO.setList(productSearchs); + + + //===============分页信息====================// + //总记录数 + long total = hits.getTotalHits().value; + esPageVO.setTotal(total); + // 总页码 + int totalPages = (int)total % pageDTO.getPageSize() == 0 ? + (int)total / pageDTO.getPageSize() : ((int)total / pageDTO.getPageSize() + 1); + esPageVO.setPages(totalPages); + return esPageVO; + } + + private List getProductSearchVOList(SearchResponse response) { + ProductSearchVO productSearchVO = new ProductSearchVO(); + //===============spu列表信息====================// + productSearchVO.setSpus(getSpuListByResponse(response.getHits().getHits())); + + //===============聚合信息====================// + Aggregations aggregations = response.getAggregations(); + if (Objects.nonNull(aggregations)) { + loadAggregationsData(productSearchVO,aggregations); + } + + List productSearches = new ArrayList<>(); + productSearches.add(productSearchVO); + return productSearches; + } + + private void loadAggregationsData(ProductSearchVO productSearchVO, Aggregations aggregations) { + //===============品牌信息====================// + ParsedLongTerms brandTerms = aggregations.get(EsConstant.BRANDS); + if (Objects.nonNull(brandTerms)) { + productSearchVO.setBrands(new ArrayList<>()); + List brandsBuckets = brandTerms.getBuckets(); + for (Terms.Bucket bucket : brandsBuckets) { + BrandSearchVO brandSearchVO = new BrandSearchVO(); + brandSearchVO.setBrandId(Long.valueOf(bucket.getKey().toString())); + brandSearchVO.setBrandImg(getValuesByBucket(bucket, EsConstant.BRAND_IMG)); + brandSearchVO.setBrandName(getValuesByBucket(bucket, EsConstant.BRAND_NAME)); + productSearchVO.getBrands().add(brandSearchVO); + } + } + //===============分类信息====================// + productSearchVO.setCategorys(new ArrayList<>()); + ParsedLongTerms categoriesTerms = null; + String categoryName = null; + // 平台分类 + if (Objects.nonNull(aggregations.get(EsConstant.CATEGORIES))) { + categoryName = EsConstant.CATEGORY_NAME; + categoriesTerms = aggregations.get(EsConstant.CATEGORIES); + } + // 店铺分类 + else { + categoryName = EsConstant.SHOP_CATEGORY_NAME; + categoriesTerms = aggregations.get(EsConstant.SHOP_CATEGORIES); + } + if (Objects.nonNull(categoriesTerms)) { + List categoriesBuckets = categoriesTerms.getBuckets(); + for (Terms.Bucket bucket : categoriesBuckets) { + CategorySearchVO categoryVO = new CategorySearchVO(); + categoryVO.setCategoryId((Long) bucket.getKey()); + categoryVO.setName(getValuesByBucket(bucket, categoryName)); + productSearchVO.getCategorys().add(categoryVO); + } + } + + //===============店铺信息====================// + ParsedLongTerms shopTerms = aggregations.get(EsConstant.SHOP); + if (Objects.nonNull(shopTerms)) { + List shopBuckets = shopTerms.getBuckets(); + for (Terms.Bucket bucket : shopBuckets) { + productSearchVO.setShopInfo(new ShopInfoSearchVO()); + productSearchVO.getShopInfo().setShopId(Long.valueOf(bucket.getKey().toString())); + } + } + //===============属性信息====================// + productSearchVO.setAttrs(new ArrayList<>()); + ParsedNested attrsNested = aggregations.get(EsConstant.ATTRS); + if (Objects.nonNull(attrsNested)) { + Aggregations attrIdAggregations = attrsNested.getAggregations(); + ParsedLongTerms attrIdsTrems = attrIdAggregations.get(EsConstant.ATTR_IDS); + List attrsBuckets = attrIdsTrems.getBuckets(); + for (Terms.Bucket bucket : attrsBuckets) { + ParsedLongTerms attrLongTerms = bucket.getAggregations().get(EsConstant.ATTR_VALUE_IDS); + AttrSearchVO attrSearchVO = null; + for (Terms.Bucket attrValueBucket : attrLongTerms.getBuckets()) { + ParsedTopHits parsedTopHits = attrValueBucket.getAggregations().get(EsConstant.TOP_HITS_DATA); + for (SearchHit hit : parsedTopHits.getHits().getHits()) { + if (Objects.isNull(attrSearchVO)) { + attrSearchVO = Json.parseObject(hit.getSourceAsString(), AttrSearchVO.class); + attrSearchVO.setAttrId(Long.valueOf(bucket.getKey().toString())); + attrSearchVO.setAttrValues(new ArrayList<>()); + } + AttrValueSearchVO attrValueSearchVO = Json.parseObject(hit.getSourceAsString(), AttrValueSearchVO.class); + attrSearchVO.getAttrValues().add(attrValueSearchVO); + } + } + productSearchVO.getAttrs().add(attrSearchVO); + } + } + } + + /** + * 从es返回的数据中获取spu列表 + * @param hits es返回的数据 + * @return + */ + public List getSpuListByResponse(SearchHit[] hits) { + List spus = new ArrayList<>(); + for (SearchHit hit : hits) { + SpuSearchVO spuSearchVO = Json.parseObject(hit.getSourceAsString(), SpuSearchVO.class); + spus.add(spuSearchVO); + } + return spus; + } + + /** + * 获取对应名称(name)的值 + * @param bucket + * @param name + * @return 仅返回一个值 + */ + private String getValuesByBucket(Terms.Bucket bucket, String name) { + String value = ""; + Aggregations categoryAggregations = bucket.getAggregations(); + ParsedStringTerms categoryNameTerms = categoryAggregations.get(name); + List buckets = categoryNameTerms.getBuckets(); + for (Terms.Bucket bucketValue : buckets) { + value = bucketValue.getKey().toString(); + break; + } + return value; + } + + /** + * 准备检索请求 + * @param pageDTO 分页参数 + * @param param 搜索参数 + * @param isAgg true:聚合搜索 false:非聚合搜索 null:非聚合搜索 + * @return + */ + private SearchRequest buildSearchRequest(EsPageDTO pageDTO,ProductSearchDTO param, Boolean isAgg) { + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + + // 指定返回数组中的字段 + searchSourceBuilder.fetchSource(EsConstant.FETCH_SOURCE, null); + + // 构建bool-query + BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); + + // 过滤 + filterQueryIfNecessary(param, boolQueryBuilder); + + // 关键字搜索 + keywordSearch(param, boolQueryBuilder); + + // 排序 + sort(param, searchSourceBuilder, boolQueryBuilder); + + //分页 + if (Objects.nonNull(pageDTO)) { + searchSourceBuilder.from((pageDTO.getPageNum()-1)*pageDTO.getPageSize()); + searchSourceBuilder.size(pageDTO.getPageSize()); + } + + // 进行聚合分析 + agg(param, searchSourceBuilder, isAgg); + + log.debug("构建的DSL语句 {}",searchSourceBuilder.toString()); + + return new SearchRequest(new String[]{EsIndexEnum.PRODUCT.value()},searchSourceBuilder); + } + + /** + * 聚合分析 + */ + private void agg(ProductSearchDTO param, SearchSourceBuilder searchSourceBuilder, Boolean isAgg) { + + // 店铺进行聚合 + if (param.getKeyword() != null && param.getKeyword().length() > 1) { + // 按照店铺进行聚合 + TermsAggregationBuilder shop = AggregationBuilders.terms(EsConstant.SHOP).field(EsConstant.SHOP_ID).size(1); + searchSourceBuilder.aggregation(shop); + } + + if (Objects.isNull(isAgg) || !isAgg) { + return; + } + + // 按照品牌进行聚合 + TermsAggregationBuilder brands = AggregationBuilders.terms(EsConstant.BRANDS).field(EsConstant.BRAND_ID).size(10); + brands.subAggregation(AggregationBuilders.terms(EsConstant.BRAND_NAME).field(EsConstant.BRAND_NAME).size(1)); + brands.subAggregation(AggregationBuilders.terms(EsConstant.BRAND_IMG).field(EsConstant.BRAND_IMG).size(1)); + searchSourceBuilder.aggregation(brands); + + // 搜索平台商品,按照平台分类信息进行聚合 + if (Objects.isNull(param.getShopId())) { + TermsAggregationBuilder categories = AggregationBuilders.terms(EsConstant.CATEGORIES).field(EsConstant.CATEGORY_ID).size(10); + categories.subAggregation(AggregationBuilders.terms(EsConstant.CATEGORY_NAME).field(EsConstant.CATEGORY_NAME).size(1)); + searchSourceBuilder.aggregation(categories); + } + // 搜索店铺中的商品,按照店铺分类信息进行聚合 + else { + TermsAggregationBuilder categories = AggregationBuilders.terms(EsConstant.SHOP_CATEGORIES).field(EsConstant.SHOP_CATEGORY_ID).size(10); + categories.subAggregation(AggregationBuilders.terms(EsConstant.SHOP_CATEGORY_NAME).field(EsConstant.SHOP_CATEGORY_NAME).size(1)); + searchSourceBuilder.aggregation(categories); + } + + // 按照属性信息进行聚合 + NestedAggregationBuilder attrs = AggregationBuilders.nested(EsConstant.ATTRS, EsConstant.ATTRS); + + // 按照属性ID进行聚合 + TermsAggregationBuilder attrIds = AggregationBuilders.terms(EsConstant.ATTR_IDS).field(EsConstant.ATTR_ATTR_ID).size(10); + attrs.subAggregation(attrIds); + TermsAggregationBuilder attrValueIds = AggregationBuilders.terms(EsConstant.ATTR_VALUE_IDS).field(EsConstant.ATTR_ATTR_VALUE_ID).size(10); + attrIds.subAggregation(attrValueIds); + String[] include1 = {EsConstant.ATTR_ATTR_NAME, EsConstant.ATTR_ATTR_VALUE_ID, EsConstant.ATTR_ATTR_VALUE_NAME}; + attrValueIds.subAggregation(AggregationBuilders + .topHits(EsConstant.TOP_HITS_DATA) + .fetchSource(include1, null) + .sort(EsConstant.ATTR_ATTR_VALUE_NAME, SortOrder.ASC) + .size(1)); + + searchSourceBuilder.aggregation(attrs); + } + + + /** + * 关键字搜索 + */ + private void keywordSearch(ProductSearchDTO param, BoolQueryBuilder boolQueryBuilder) { + if (StrUtil.isBlank(param.getKeyword())) { + return; + } + // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可 + BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery() + // 提升商品名称搜索的权重 + .should(QueryBuilders.matchQuery(EsConstant.SPU_NAME, param.getKeyword()).boost(6)); + + if (param.getKeyword().length()>1) { + // 卖点,不分词 + keywordShouldQuery.should(QueryBuilders.matchPhraseQuery(EsConstant.SELLING_POINT, param.getKeyword()).boost(3)) + // 店铺名,不分词 + .should(QueryBuilders.matchPhraseQuery(EsConstant.SHOP_NAME, param.getKeyword())); + } + boolQueryBuilder.must(keywordShouldQuery); + } + + /** + * 进行排序 + */ + private void sort(ProductSearchDTO param, SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) { + //排序 如果排序规则设为空,则按照一定的算分规则进行排序,否则按照用户指定排序规则进行排序 + if(Objects.isNull(param.getSort())){ + + List filterFunctionBuilders = new ArrayList<>(); + + // 销量数 log1p + ScoreFunctionBuilder saleNumScoreFunction = new FieldValueFactorFunctionBuilder(EsConstant.SALE_NUM).modifier(FieldValueFactorFunction.Modifier.LOG1P).factor(0.1f); + filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(saleNumScoreFunction)); + + filterFunctionBuilders.toArray(); + + FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder, ArrayUtil.toArray(filterFunctionBuilders, FunctionScoreQueryBuilder.FilterFunctionBuilder.class)) + .scoreMode(FunctionScoreQuery.ScoreMode.SUM).boostMode(CombineFunction.SUM); + + + //封装所有的查询条件(带有function score) + searchSourceBuilder.query(functionScoreQueryBuilder); + + } else { + for (EsProductSortEnum enumValue : EsProductSortEnum.values()) { + if (!Objects.equals(enumValue.value(), param.getSort())) { + continue; + } + if (EsProductSortEnum.isAsc(enumValue)) { + searchSourceBuilder.sort(enumValue.sort(), SortOrder.ASC); + } else if (EsProductSortEnum.isDesc(enumValue)) { + searchSourceBuilder.sort(enumValue.sort(),SortOrder.DESC); + } else { + + } + } + //封装所有的查询条件(没有function score) + searchSourceBuilder.query(boolQueryBuilder); + } + } + + /** + * 过滤查询条件,如果有必要的话 + * @param param 查询条件 + * @param boolQueryBuilder 组合进boolQueryBuilder + */ + private void filterQueryIfNecessary(ProductSearchDTO param, BoolQueryBuilder boolQueryBuilder) { + + // 店铺id + if(Objects.nonNull(param.getShopId())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_ID, param.getShopId())); + } + + // spu状态 + List statusList = new ArrayList<>(); + if (Objects.nonNull(param.getSpuStatus())) { + statusList.add(param.getSpuStatus()); + } else if (Objects.equals(param.getDataType(), DataType.SALE.value())) { + statusList.add(StatusEnum.ENABLE.value()); + } else if (Objects.equals(param.getDataType(), DataType.DISABLE.value())) { + statusList.add(StatusEnum.DISABLE.value()); + } else { + statusList.add(StatusEnum.ENABLE.value()); + statusList.add(StatusEnum.DISABLE.value()); + } + if (statusList.size() != 0) { + BoolQueryBuilder should = QueryBuilders.boolQuery().should(QueryBuilders.termsQuery(EsConstant.SPU_STATUS, statusList)); + boolQueryBuilder.filter(should); + } + + this.categoryFilterQuery(param, boolQueryBuilder); + + // 是否有库存 + if(Objects.nonNull(param.getHasStock())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.HAS_STOCK,BooleanUtil.isTrue(param.getHasStock()))); + } + + // 商品类型 + if(Objects.nonNull(param.getSelfShop())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_TYPE, param.getSelfShop())); + } + + // 品牌 + if(StrUtil.isNotBlank(param.getBrandIds())){ + String[] ids = param.getBrandIds().split(EsConstant.CONNECTION_SYMBOLS); + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + for (String brandId : ids) { + boolQuery.should(QueryBuilders.termsQuery(EsConstant.BRAND_ID, brandId)); + boolQueryBuilder.filter(boolQuery); + } + } + + // spuId列表 + if(CollectionUtil.isNotEmpty(param.getSpuIds())){ + boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.SPU_ID,param.getSpuIds())); + } + + // 规格属性值 + if(StrUtil.isNotBlank(param.getAttrValueIds())){ + String[] ids = param.getAttrValueIds().split(EsConstant.CONNECTION_SYMBOLS); + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + for (String attrValueId : ids) { + boolQuery.should(QueryBuilders.termsQuery(EsConstant.ATTR_ATTR_VALUE_ID, attrValueId)); + } + NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(EsConstant.ATTRS,boolQuery, ScoreMode.None); + boolQueryBuilder.filter(nestedQueryBuilder); + } + + // 价格区间 + if(param.getMinPrice() != null || param.getMaxPrice() != null){ + RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(EsConstant.PRICE_FEE); + if(param.getMinPrice() != null){ + rangeQueryBuilder.gte(param.getMinPrice()); + } + if(param.getMaxPrice() != null){ + rangeQueryBuilder.lte(param.getMaxPrice()); + } + boolQueryBuilder.filter(rangeQueryBuilder); + } + + // 销量区间 + if(param.getMinSaleNum() != null || param.getMaxSaleNum() != null){ + RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(EsConstant.SALE_NUM); + if(param.getMinSaleNum() != null){ + rangeQueryBuilder.gte(param.getMinSaleNum()); + } + if(param.getMaxSaleNum() != null){ + rangeQueryBuilder.lte(param.getMaxSaleNum()); + } + boolQueryBuilder.filter(rangeQueryBuilder); + } + } + + private void categoryFilterQuery(ProductSearchDTO param, BoolQueryBuilder boolQueryBuilder){ + // 商家一级分类 + if(Objects.nonNull(param.getShopPrimaryCategoryId())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_PRIMARY_CATEGORY_ID, param.getShopPrimaryCategoryId())); + } + + //商家二级分类 + if(Objects.nonNull(param.getShopSecondaryCategoryId())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.SHOP_SECONDARY_CATEGORY_ID, param.getShopSecondaryCategoryId())); + } + + // 平台一级分类 + if(Objects.nonNull(param.getPrimaryCategoryId())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.PRIMARY_CATEGORY_ID, param.getPrimaryCategoryId())); + } + + // 平台三级分类 + if(Objects.nonNull(param.getCategoryId())){ + boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.CATEGORY_ID,param.getCategoryId())); + } + } + + /** + * 根据店铺id列表获取每个店铺的spu列表 + * @param shopIds + * @return + */ + public List limitSizeListByShopIds(List shopIds, Integer size) { + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + + // 构建bool-query + BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); + // 过滤 + boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.SHOP_ID,shopIds)); + searchSourceBuilder.query(boolQueryBuilder); + // 聚合分析 + TermsAggregationBuilder shop = AggregationBuilders.terms(EsConstant.SHOP_COUPON) + .field(EsConstant.SHOP_ID); + String[] include = {EsConstant.SPU_NAME, EsConstant.MAIN_IMG_URL,EsConstant.SHOP_ID, EsConstant.SPU_ID, EsConstant.PRICE_FEE}; + shop.subAggregation(AggregationBuilders + .topHits(EsConstant.TOP_HITS_DATA) + .fetchSource(include, null) + .sort(EsConstant.SALE_NUM, SortOrder.DESC) + .size(size)); + searchSourceBuilder.aggregation(shop); + searchSourceBuilder.size(0); + log.debug("构建的DSL语句 {}",searchSourceBuilder.toString()); + SearchRequest searchRequest = new SearchRequest(new String[]{EsIndexEnum.PRODUCT.value()}, searchSourceBuilder); + //2、执行检索请求 + SearchResponse response = null; + List spuList = null; + try { + response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); + log.debug("搜索返回结果:" + response.toString()); + spuList = loadSpuListByAggregations(response); + } catch (IOException e) { + e.printStackTrace(); + } + return spuList; + } + + /** + * 从聚合数据中获取商品列表 + * @param response + * @return + */ + private List loadSpuListByAggregations(SearchResponse response) { + List spuList = new ArrayList<>(); + Aggregations aggregations = response.getAggregations(); + ParsedLongTerms shopCouponTerm = aggregations.get(EsConstant.SHOP_COUPON); + if (Objects.nonNull(shopCouponTerm)) { + List buckets = shopCouponTerm.getBuckets(); + for (Terms.Bucket bucket : buckets ) { + Aggregations shopAggs = bucket.getAggregations(); + ParsedTopHits shopHits = shopAggs.get(EsConstant.TOP_HITS_DATA); + spuList.addAll(getSpuListByResponse(shopHits.getHits().getHits())); + } + } + return spuList; + } + + /** + * 商品管理分页搜索es数据的信息 + * @param pageDTO 分页数据 + * @param productSearchDTO 商品搜索条件 + * @return 搜索结果 + */ + public EsPageVO adminPage(EsPageDTO pageDTO, ProductSearchDTO productSearchDTO) { + loadSpuStatus(productSearchDTO); + EsPageVO result = new EsPageVO<>(); + SearchResponse response = pageSearchResult(pageDTO, productSearchDTO, Boolean.FALSE); + // 商品信息 + result.setList(buildSpuAdminList(response)); + // 分页信息 + buildSearchPage(pageDTO, result, response); + return result; + } + + private void loadSpuStatus(ProductSearchDTO productSearchDTO) { + if (Objects.isNull(productSearchDTO) || Objects.isNull(productSearchDTO.getDataType())) { + return; + } + Integer dataType = productSearchDTO.getDataType(); + // 销售中 + if (Objects.equals(DataType.SALE.value(), dataType)) { + productSearchDTO.setSpuStatus(StatusEnum.ENABLE.value()); + } + // 已售罄 + else if (Objects.equals(DataType.SOLD_OUT.value(), dataType)) { + productSearchDTO.setHasStock(0); + } + } + + /** + * 从es返回的数据中获取spu列表 + * @param response es返回的数据 + * @return + */ + public List buildSpuAdminList(SearchResponse response) { + List spus = new ArrayList<>(); + String spuName = null; + for (SearchHit hit : response.getHits()) { + String json = hit.getSourceAsString(); + SpuAdminVO spuSearchVO = Json.parseObject(json, SpuAdminVO.class); + spus.add(spuSearchVO); + } + return spus; + } + + /** + * 处理聚合国际化信息 + * @param json 数据 + * @param field 字段 + * @return 对应语言的字段 + */ + private String handleAggregationsLang(String json, String field, String defaultField) { + Map map = Json.parseObject(json, Map.class); + Object object; + // 找不到指定语言的数据,就查默认语言 + if (Objects.isNull(map.get(field))) { + object = map.get(defaultField); + } + // 获取指定语言的数据 + else { + object = map.get(field); + } + // 没有查到数据 + if (Objects.isNull(object)) { + return null; + } + return object.toString(); + } + + /** + * 构建分页数据 + * @param pageDTO + * @param esPageVO + * @param response + */ + private void buildSearchPage(EsPageDTO pageDTO, EsPageVO esPageVO, SearchResponse response) { + //总记录数 + long total = response.getHits().getTotalHits().value; + esPageVO.setTotal(total); + // 总页码 + int totalPages = (int)total % pageDTO.getPageSize() == 0 ? + (int)total / pageDTO.getPageSize() : ((int)total / pageDTO.getPageSize() + 1); + esPageVO.setPages(totalPages); + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductUpdateManager.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductUpdateManager.java new file mode 100644 index 00000000..3f1c7f74 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/manager/ProductUpdateManager.java @@ -0,0 +1,50 @@ +package com.mall4j.cloud.search.manager; + +import com.mall4j.cloud.api.product.bo.EsProductBO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.util.Json; +import com.mall4j.cloud.search.constant.EsIndexEnum; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author FrozenWatermelon + * @date 2020/11/16 + */ +@Component +public class ProductUpdateManager { + + @Autowired + private RestHighLevelClient restHighLevelClient; + + /** + * 批量更新es中的商品信息 + * @param spuIds spuId列表 + * @param esProductBO 更新的数据 + */ + public void esUpdateSpuBySpuIds(List spuIds, EsProductBO esProductBO) { + String source = Json.toJsonString(esProductBO); + try { + BulkRequest request = new BulkRequest(); + // 准备更新的数据 + for (Long spuId : spuIds) { + request.add(new UpdateRequest(EsIndexEnum.PRODUCT.value(), String.valueOf(spuId)).doc(source, XContentType.JSON)); + } + //更新 + BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT); + if (bulkResponse.hasFailures()) { + throw new mall4cloudException(bulkResponse.buildFailureMessage()); + } + } catch (Exception e) { + throw new mall4cloudException(e.getMessage()); + } + } +} diff --git a/mall4cloud-search/src/main/java/com/mall4j/cloud/search/vo/SpuAdminVO.java b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/vo/SpuAdminVO.java new file mode 100644 index 00000000..f3e610f6 --- /dev/null +++ b/mall4cloud-search/src/main/java/com/mall4j/cloud/search/vo/SpuAdminVO.java @@ -0,0 +1,147 @@ +package com.mall4j.cloud.search.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @Author lth + * @Date 2021/6/21 10:38 + */ +public class SpuAdminVO { + @ApiModelProperty(value = "商品id") + private Long spuId; + + @ApiModelProperty(value = "商品名称") + private String spuName; + + @ApiModelProperty("商品介绍主图") + private String mainImgUrl; + + @ApiModelProperty("店铺id") + private Long shopId; + + @ApiModelProperty("店铺名称") + private String shopName; + + @ApiModelProperty(value = "商品售价") + private Long priceFee; + + @ApiModelProperty(value = "市场价,整数方式保存") + private Long marketPriceFee; + + @ApiModelProperty(value = "销量") + private Integer saleNum; + + @ApiModelProperty(value = "状态") + private Integer spuStatus; + + @ApiModelProperty(value = "库存") + private Integer stock; + + @ApiModelProperty(value = "序号") + private Integer seq; + + public Long getSpuId() { + return spuId; + } + + public void setSpuId(Long spuId) { + this.spuId = spuId; + } + + public String getSpuName() { + return spuName; + } + + public void setSpuName(String spuName) { + this.spuName = spuName; + } + + public String getMainImgUrl() { + return mainImgUrl; + } + + public void setMainImgUrl(String mainImgUrl) { + this.mainImgUrl = mainImgUrl; + } + + public Long getShopId() { + return shopId; + } + + public void setShopId(Long shopId) { + this.shopId = shopId; + } + + public String getShopName() { + return shopName; + } + + public void setShopName(String shopName) { + this.shopName = shopName; + } + + public Long getPriceFee() { + return priceFee; + } + + public void setPriceFee(Long priceFee) { + this.priceFee = priceFee; + } + + public Long getMarketPriceFee() { + return marketPriceFee; + } + + public void setMarketPriceFee(Long marketPriceFee) { + this.marketPriceFee = marketPriceFee; + } + + public Integer getSaleNum() { + return saleNum; + } + + public void setSaleNum(Integer saleNum) { + this.saleNum = saleNum; + } + + public Integer getSpuStatus() { + return spuStatus; + } + + public void setSpuStatus(Integer spuStatus) { + this.spuStatus = spuStatus; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + public Integer getSeq() { + return seq; + } + + public void setSeq(Integer seq) { + this.seq = seq; + } + + @Override + public String toString() { + return "SpuAdminVO{" + + "spuId=" + spuId + + ", spuName='" + spuName + '\'' + + ", mainImgUrl='" + mainImgUrl + '\'' + + ", shopId=" + shopId + + ", shopName='" + shopName + '\'' + + ", priceFee=" + priceFee + + ", marketPriceFee=" + marketPriceFee + + ", saleNum=" + saleNum + + ", spuStatus=" + spuStatus + + ", stock=" + stock + + ", seq=" + seq + + '}'; + } +} diff --git a/mall4cloud-search/src/main/resources/bootstrap.yml b/mall4cloud-search/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..bc290375 --- /dev/null +++ b/mall4cloud-search/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9108 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-user/pom.xml b/mall4cloud-user/pom.xml new file mode 100644 index 00000000..8a4f77f8 --- /dev/null +++ b/mall4cloud-user/pom.xml @@ -0,0 +1,66 @@ + + + + mall4cloud + com.mall4j.cloud + 1.0-SNAPSHOT + + 4.0.0 + + mall4cloud-user + mall4cloud 用户服务 + jar + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + com.mall4j.cloud + mall4cloud-common-database + ${project.version} + + + com.mall4j.cloud + mall4cloud-common-security + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-user + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-platform + ${project.version} + + + com.mall4j.cloud + mall4cloud-api-biz + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/UserApplication.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/UserApplication.java new file mode 100644 index 00000000..3fe653ef --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/UserApplication.java @@ -0,0 +1,19 @@ +package com.mall4j.cloud.user; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author FrozenWatermelon + * @date 2020/11/19 + */ +@SpringBootApplication(scanBasePackages = { "com.mall4j.cloud" }) +@EnableFeignClients(basePackages = {"com.mall4j.cloud.api.**.feign"}) +public class UserApplication { + + public static void main(String[] args) { + SpringApplication.run(UserApplication.class, args); + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/config/SwaggerConfiguration.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/config/SwaggerConfiguration.java new file mode 100644 index 00000000..eabaf101 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/config/SwaggerConfiguration.java @@ -0,0 +1,38 @@ +package com.mall4j.cloud.user.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger文档,只有在测试环境才会使用 + * + * @author FrozenWatermelon + */ +@Configuration +@EnableSwagger2 +@EnableKnife4j +public class SwaggerConfiguration { + + @Bean + public Docket baseRestApi() { + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + .apis(RequestHandlerSelectors.basePackage("com.mall4j.cloud.user.controller")).paths(PathSelectors.any()) + .build(); + } + + @Bean + public ApiInfo apiInfo() { + return new ApiInfoBuilder().title("mall4cloud商城接口文档").description("mall4cloud商城接口文档Swagger版").termsOfServiceUrl("") + .contact(new Contact("广州市蓝海创新科技有限公司", "", "")).version("1.0").build(); + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/AreaController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/AreaController.java new file mode 100644 index 00000000..2b4a7ec8 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/AreaController.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.user.controller.app; + +import com.mall4j.cloud.api.user.vo.AreaVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.user.service.AreaService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +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; + +/** + * 省市区地区信息 + * + * @author YXF + * @date 2020-11-25 14:48:52 + */ +@RestController("appAreaController") +@RequestMapping("/area") +@Api(tags = "app-地区信息") +public class AreaController { + + @Autowired + private AreaService areaService; + + @GetMapping("/list") + @ApiOperation(value = "获取省市区地区信息列表", notes = "获取省市区地区信息列表") + public ServerResponseEntity> list() { + List list = areaService.getAreaListInfo(); + return ServerResponseEntity.success(list); + } + + @GetMapping("/list_by_pid") + @ApiOperation(value = "通过父级id获取区域列表", notes = "通过父级id获取区域列表") + public ServerResponseEntity> listByPid(Long pid) { + List list = areaService.listByPid(pid); + return ServerResponseEntity.success(list); + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserAddrController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserAddrController.java new file mode 100644 index 00000000..636130af --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserAddrController.java @@ -0,0 +1,111 @@ +package com.mall4j.cloud.user.controller.app; + +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.user.dto.UserAddrDTO; +import com.mall4j.cloud.user.model.UserAddr; +import com.mall4j.cloud.user.service.UserAddrService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * @Author lth + * @Date 2021/7/1 17:18 + */ +@RestController("appUserAddrController") +@RequestMapping("/user_addr") +@Api(tags = "app-用户地址") +public class UserAddrController { + + @Autowired + private UserAddrService userAddrService; + + @Autowired + private MapperFacade mapperFacade; + + private static final Integer MAX_USER_ADDR = 10; + + @GetMapping("/list") + @ApiOperation(value = "获取用户地址列表", notes = "获取用户地址列表") + public ServerResponseEntity> list() { + Long userId = AuthUserContext.get().getUserId(); + List userAddrPage = userAddrService.list(userId); + return ServerResponseEntity.success(userAddrPage); + } + + @GetMapping + @ApiOperation(value = "获取用户地址", notes = "根据addrId获取用户地址") + public ServerResponseEntity getByAddrId(@RequestParam Long addrId) { + return ServerResponseEntity.success(userAddrService.getUserAddrByUserId(addrId,AuthUserContext.get().getUserId())); + } + + @PostMapping + @ApiOperation(value = "保存用户地址", notes = "保存用户地址") + public ServerResponseEntity save(@Valid @RequestBody UserAddrDTO userAddrDTO) { + Long userId = AuthUserContext.get().getUserId(); + int userAddrCount = userAddrService.countByUserId(userId); + if (userAddrCount >= MAX_USER_ADDR) { + return ServerResponseEntity.showFailMsg("收货地址已达到上限,无法再新增地址"); + } + UserAddr userAddr = mapperFacade.map(userAddrDTO, UserAddr.class); + if (userAddrCount == 0) { + userAddr.setIsDefault(UserAddr.DEFAULT_ADDR); + } else if (!UserAddr.DEFAULT_ADDR.equals(userAddr.getIsDefault())){ + userAddr.setIsDefault(UserAddr.NOT_DEFAULT_ADDR); + } + userAddr.setAddrId(null); + userAddr.setUserId(userId); + userAddrService.save(userAddr); + // 清除默认地址缓存 + if (UserAddr.DEFAULT_ADDR.equals(userAddr.getIsDefault())) { + userAddrService.removeUserDefaultAddrCacheByUserId(userId); + } + return ServerResponseEntity.success(); + } + + @PutMapping + @ApiOperation(value = "更新用户地址", notes = "更新用户地址") + public ServerResponseEntity update(@Valid @RequestBody UserAddrDTO userAddrDTO) { + Long userId = AuthUserContext.get().getUserId(); + UserAddrVO dbUserAddr = userAddrService.getUserAddrByUserId(userAddrDTO.getAddrId(), userId); + if (dbUserAddr == null) { + throw new mall4cloudException("该地址已被删除"); + } + // 默认地址不能修改为普通地址 + else if (dbUserAddr.getIsDefault().equals(UserAddr.DEFAULT_ADDR) && userAddrDTO.getIsDefault().equals(UserAddr.NOT_DEFAULT_ADDR)) { + throw new mall4cloudException(ResponseEnum.DATA_ERROR); + } + UserAddr userAddr = mapperFacade.map(userAddrDTO, UserAddr.class); + userAddr.setUserId(userId); + userAddrService.update(userAddr); + // 清除默认地址缓存 + if (userAddr.getIsDefault().equals(UserAddr.DEFAULT_ADDR)) { + userAddrService.removeUserDefaultAddrCacheByUserId(userId); + } + return ServerResponseEntity.success(); + } + + @DeleteMapping + @ApiOperation(value = "删除用户地址", notes = "根据用户地址id删除用户地址") + public ServerResponseEntity delete(@RequestParam Long addrId) { + Long userId = AuthUserContext.get().getUserId(); + UserAddrVO dbUserAddr = userAddrService.getUserAddrByUserId(addrId, userId); + if (dbUserAddr == null) { + throw new mall4cloudException("该地址已被删除"); + } else if (dbUserAddr.getIsDefault().equals(UserAddr.DEFAULT_ADDR)) { + throw new mall4cloudException("默认地址不能删除"); + } + userAddrService.deleteUserAddrByUserId(addrId, userId); + return ServerResponseEntity.success(); + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserController.java new file mode 100644 index 00000000..abf7c248 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserController.java @@ -0,0 +1,70 @@ +package com.mall4j.cloud.user.controller.app; + +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.user.model.User; +import com.mall4j.cloud.user.service.UserService; +import com.mall4j.cloud.user.vo.UserSimpleInfoVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Objects; + +/** + * 用户地址 + * + * @author FrozenWatermelon + * @date 2020-12-07 15:50:02 + */ +@RestController("appUserController") +@RequestMapping("/a/user") +@Api(tags = "app-用户信息") +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/simple_info") + @ApiOperation(value="用户头像昵称", notes="用户头像昵称") + public ServerResponseEntity getByAddrId() { + Long userId = AuthUserContext.get().getUserId(); + + UserApiVO userApiVO = userService.getByUserId(userId); + UserSimpleInfoVO userSimpleInfoVO = new UserSimpleInfoVO(); + userSimpleInfoVO.setNickName(userApiVO.getNickName()); + userSimpleInfoVO.setPic(userApiVO.getPic()); + + return ServerResponseEntity.success(userSimpleInfoVO); + } + + + @GetMapping("/ma/user_detail_info") + @ApiOperation(value = "获取用户详细信息", notes = "返回用户详细信息") + public ServerResponseEntity getUserDetailInfo() { + UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get(); + if (userInfoInTokenBO == null) { + return ServerResponseEntity.fail(ResponseEnum.CLEAN_TOKEN); + } + Long userId = userInfoInTokenBO.getUserId(); + UserApiVO userApiVO = userService.getByUserId(userId); + return ServerResponseEntity.success(userApiVO); + } + + @PostMapping ("/ma/update_user") + @ApiOperation(value = "更新用户信息") + public ServerResponseEntity updateUser(@RequestBody UserApiVO userApiVO) { + Long userId = AuthUserContext.get().getUserId(); + UserApiVO byUserId = userService.getByUserId(userId); + User user = new User(); + user.setUserId(userId); + user.setNickName(Objects.isNull(userApiVO.getNickName())? byUserId.getNickName() : userApiVO.getNickName()); + user.setPic(Objects.isNull(userApiVO.getPic())? byUserId.getPic() : userApiVO.getPic()); + userService.update(user); + return ServerResponseEntity.success(); + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserRegisterController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserRegisterController.java new file mode 100644 index 00000000..769ff357 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/app/UserRegisterController.java @@ -0,0 +1,54 @@ +package com.mall4j.cloud.user.controller.app; + + +import cn.hutool.core.util.StrUtil; +import com.mall4j.cloud.api.auth.bo.UserInfoInTokenBO; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.api.auth.vo.TokenInfoVO; +import com.mall4j.cloud.user.dto.UserRegisterDTO; +import com.mall4j.cloud.user.service.UserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * 用户信息 + * @author FrozenWatermelon + */ +@RestController +@RequestMapping("/ua/user/register") +@Api(tags="app-用户注册接口") +public class UserRegisterController { + + @Autowired + private UserService userService; + @Autowired + private AccountFeignClient accountFeignClient; + + @ApiOperation(value="注册") + @PostMapping + public ServerResponseEntity register(@Valid @RequestBody UserRegisterDTO param) { + + if (StrUtil.isBlank(param.getNickName())) { + param.setNickName(param.getUserName()); + } + // 1. 保存账户信息 + Long uid = userService.save(param); + // 2. 登录 + UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO(); + userInfoInTokenBO.setUid(uid); + userInfoInTokenBO.setUserId(param.getUserId()); + userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value()); + userInfoInTokenBO.setIsAdmin(0); + return accountFeignClient.storeTokenAndGetVo(userInfoInTokenBO); + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/multishop/UserController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/multishop/UserController.java new file mode 100644 index 00000000..fb827ec5 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/controller/multishop/UserController.java @@ -0,0 +1,57 @@ +package com.mall4j.cloud.user.controller.multishop; + +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.user.model.User; +import com.mall4j.cloud.user.service.UserService; +import com.mall4j.cloud.user.dto.UserDTO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + +/** + * 用户表 + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +@RestController("multishopUserController") +@RequestMapping("/m/user") +@Api(tags = "店铺-用户表") +public class UserController { + + @Autowired + private UserService userService; + + @Autowired + private MapperFacade mapperFacade; + + @GetMapping("/page") + @ApiOperation(value = "获取用户表列表", notes = "分页获取用户表列表") + public ServerResponseEntity> page(@Valid PageDTO pageDTO) { + PageVO userPage = userService.page(pageDTO); + return ServerResponseEntity.success(userPage); + } + + @GetMapping + @ApiOperation(value = "获取用户表", notes = "根据userId获取用户表") + public ServerResponseEntity getByUserId(@RequestParam Long userId) { + UserApiVO userVO = mapperFacade.map(userService.getByUserId(userId), UserApiVO.class); + return ServerResponseEntity.success(userVO); + } + + + @PutMapping + @ApiOperation(value = "更新用户表", notes = "更新用户表") + public ServerResponseEntity update(@Valid @RequestBody UserDTO userDTO) { + User user = mapperFacade.map(userDTO, User.class); + userService.update(user); + return ServerResponseEntity.success(); + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserAddrDTO.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserAddrDTO.java new file mode 100644 index 00000000..ba317230 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserAddrDTO.java @@ -0,0 +1,189 @@ +package com.mall4j.cloud.user.dto; + +import io.swagger.annotations.ApiModelProperty; +import org.hibernate.validator.constraints.Length; + +/** + * 用户地址DTO + * + * @author FrozenWatermelon + * @date 2020-12-07 15:50:02 + */ +public class UserAddrDTO { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long addrId; + + @ApiModelProperty("手机") + private String mobile; + + @ApiModelProperty("是否默认地址 1是") + private Integer isDefault; + + @ApiModelProperty("收货人") + @Length(min = 2, max = 20, message = "收货人姓名需要在2到20个字符之间") + private String consignee; + + @ApiModelProperty("省ID") + private Long provinceId; + + @ApiModelProperty("省") + private String province; + + @ApiModelProperty("城市ID") + private Long cityId; + + @ApiModelProperty("城市") + private String city; + + @ApiModelProperty("区ID") + private Long areaId; + + @ApiModelProperty("区") + private String area; + + @ApiModelProperty("邮编") + private String postCode; + + @ApiModelProperty("地址") + private String addr; + + @ApiModelProperty("经度") + private Double lng; + + @ApiModelProperty("纬度") + private Double lat; + + public Long getAddrId() { + return addrId; + } + + public void setAddrId(Long addrId) { + this.addrId = addrId; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getIsDefault() { + return isDefault; + } + + public void setIsDefault(Integer isDefault) { + this.isDefault = isDefault; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public Long getProvinceId() { + return provinceId; + } + + public void setProvinceId(Long provinceId) { + this.provinceId = provinceId; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public Long getCityId() { + return cityId; + } + + public void setCityId(Long cityId) { + this.cityId = cityId; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getPostCode() { + return postCode; + } + + public void setPostCode(String postCode) { + this.postCode = postCode; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public Double getLng() { + return lng; + } + + public void setLng(Double lng) { + this.lng = lng; + } + + public Double getLat() { + return lat; + } + + public void setLat(Double lat) { + this.lat = lat; + } + + @Override + public String toString() { + return "UserAddrDTO{" + + "addrId=" + addrId + + ",mobile=" + mobile + + ",isDefault=" + isDefault + + ",consignee=" + consignee + + ",provinceId=" + provinceId + + ",province=" + province + + ",cityId=" + cityId + + ",city=" + city + + ",areaId=" + areaId + + ",area=" + area + + ",postCode=" + postCode + + ",addr=" + addr + + ",lng=" + lng + + ",lat=" + lat + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserDTO.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserDTO.java new file mode 100644 index 00000000..109791c4 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserDTO.java @@ -0,0 +1,68 @@ +package com.mall4j.cloud.user.dto; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Date; + +/** + * 用户表DTO + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public class UserDTO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long userId; + + @ApiModelProperty("用户昵称") + private String nickName; + + @ApiModelProperty("头像图片路径") + private String pic; + + @ApiModelProperty("状态 1 正常 0 无效") + private Integer status; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "UserDTO{" + + "userId=" + userId + + ",nickName=" + nickName + + ",pic=" + pic + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserRegisterDTO.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserRegisterDTO.java new file mode 100644 index 00000000..89a8a100 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/dto/UserRegisterDTO.java @@ -0,0 +1,94 @@ +package com.mall4j.cloud.user.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; + +/** + * @author lhd + * @date 2020/12/30 + */ +@ApiModel(value= "用户注册信息") +public class UserRegisterDTO { + + @NotBlank + @ApiModelProperty(value = "密码") + private String password; + + @ApiModelProperty(value = "头像") + private String img; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @NotBlank + @ApiModelProperty(value = "用户名") + private String userName; + + @ApiModelProperty(value = "当账户未绑定时,临时的uid") + private String tempUid; + + @ApiModelProperty(value = "用户id") + private Long userId; + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getImg() { + return img; + } + + public void setImg(String img) { + this.img = img; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getTempUid() { + return tempUid; + } + + public void setTempUid(String tempUid) { + this.tempUid = tempUid; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + @Override + public String toString() { + return "UserRegisterDTO{" + + "password='" + password + '\'' + + ", img='" + img + '\'' + + ", nickName='" + nickName + '\'' + + ", userName='" + userName + '\'' + + ", tempUid='" + tempUid + '\'' + + ", userId=" + userId + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserAddrFeignController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserAddrFeignController.java new file mode 100644 index 00000000..ec09ec2a --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserAddrFeignController.java @@ -0,0 +1,26 @@ +package com.mall4j.cloud.user.feign; + +import com.mall4j.cloud.api.user.feign.UserAddrFeignClient; +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.security.AuthUserContext; +import com.mall4j.cloud.user.service.UserAddrService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +/** + * 用户地址feign连接 + * @author FrozenWatermelon + * @date 2020/12/07 + */ +@RestController +public class UserAddrFeignController implements UserAddrFeignClient { + + @Autowired + private UserAddrService userAddrService; + + @Override + public ServerResponseEntity getUserAddrByAddrId(Long addrId) { + return ServerResponseEntity.success(userAddrService.getUserAddrByUserId(addrId,AuthUserContext.get().getUserId())); + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserFeignController.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserFeignController.java new file mode 100644 index 00000000..b2c48ade --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/feign/UserFeignController.java @@ -0,0 +1,42 @@ +package com.mall4j.cloud.user.feign; + +import com.mall4j.cloud.api.user.feign.UserFeignClient; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.user.service.UserService; +import com.mall4j.cloud.api.user.vo.UserApiVO; +import ma.glasnost.orika.MapperFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 用户地址feign连接 + * @author FrozenWatermelon + * @date 2020/12/07 + */ +@RestController +public class UserFeignController implements UserFeignClient { + + @Autowired + private UserService userService; + @Autowired + private MapperFacade mapperFacade; + + @Override + public ServerResponseEntity> getUserByUserIds(List userIds) { + List userList = userService.getUserByUserIds(userIds); + return ServerResponseEntity.success(userList); + } + + @Override + public ServerResponseEntity getUserData(Long userId) { + UserApiVO user = userService.getByUserId(userId); + return ServerResponseEntity.success(user); + } + + @Override + public ServerResponseEntity getUserAndOpenIdsByUserId(Long userId) { + return ServerResponseEntity.success(userService.getUserAndOpenIdsByUserId(userId)); + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/AreaMapper.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/AreaMapper.java new file mode 100644 index 00000000..25be7a83 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/AreaMapper.java @@ -0,0 +1,81 @@ +package com.mall4j.cloud.user.mapper; + +import com.mall4j.cloud.api.user.vo.AreaVO; +import com.mall4j.cloud.user.model.Area; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 省市区地区信息 + * + * @author YXF + * @date 2020-11-25 15:10:06 + */ +public interface AreaMapper { + + /** + * 获取省市区地区信息列表 + * + * @return 省市区地区信息列表 + */ + List list(); + + /** + * 根据省市区地区信息id获取省市区地区信息 + * + * @param areaId 省市区地区信息id + * @return 省市区地区信息 + */ + AreaVO getByAreaId(@Param("areaId") Long areaId); + + /** + * 保存省市区地区信息 + * + * @param area 省市区地区信息 + */ + void save(@Param("area") Area area); + + /** + * 更新省市区地区信息 + * + * @param area 省市区地区信息 + */ + void update(@Param("area") Area area); + + /** + * 根据省市区地区信息id删除省市区地区信息 + * + * @param areaId + */ + void deleteById(@Param("areaId") Long areaId); + + /** + * 获取该地址id下的下级地址数量 + * + * @param areaId + * @return + */ + int countByAreaId(@Param("areaId") Long areaId); + + /** + * 根据上级分类id获取下级地址列表 + * + * @param pid + * @return + */ + List listByPid(@Param("pid") Long pid); + + /** + * 获取省市区三级结构完整的集合 + * + * @return 省市区三级结构完整的集合 + */ + List getAreaListInfo(); + + /** + * 获取可用的省市区列表 + * @return + */ + List listAreaOfEnable(); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserAddrMapper.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserAddrMapper.java new file mode 100644 index 00000000..00d33a04 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserAddrMapper.java @@ -0,0 +1,84 @@ +package com.mall4j.cloud.user.mapper; + +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.user.model.UserAddr; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @Author lth + * @Date 2021/7/1 17:42 + */ +public interface UserAddrMapper { + + /** + * 获取用户地址列表 + * + * @param userId + * @return 用户地址列表 + */ + List list(@Param("userId") Long userId); + + /** + * 根据用户地址id获取用户地址 + * + * @param userId 用户id + * @param addrId 用户地址id + * @return 用户地址 + */ + UserAddrVO getByAddrId(@Param("addrId") Long addrId, @Param("userId") Long userId); + + /** + * 保存用户地址 + * + * @param userAddr 用户地址 + */ + void save(@Param("userAddr") UserAddr userAddr); + + /** + * 更新用户地址 + * + * @param userAddr 用户地址 + */ + void update(@Param("userAddr") UserAddr userAddr); + + /** + * 删除地址 + * + * @param addrId 地址id + * @param userId 用户id + */ + void deleteById(@Param("addrId") Long addrId, @Param("userId") Long userId); + + + /** + * 移除用户默认地址 + * + * @param userId + */ + void removeDefaultUserAddr(@Param("userId") Long userId); + + /** + * 将地址设置为默认地址 + * + * @param addrId 地址id + * @param userId 用户id + */ + void setDefaultUserAddr(@Param("addrId") Long addrId, @Param("userId") Long userId); + + /** + * 用户地址的数量 + * + * @param userId 用户id + * @return 数量 + */ + int countByUserId(Long userId); + + /** + * 通过用户id获取默认地址 + * @param userId 用户id + * @return 默认地址 + */ + UserAddrVO getUserDefaultAddrByUserId(@Param("userId") Long userId); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserMapper.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserMapper.java new file mode 100644 index 00000000..e03200f4 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/mapper/UserMapper.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.user.mapper; + +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.user.model.User; +import com.mall4j.cloud.user.vo.UserVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public interface UserMapper { + + /** + * 获取用户表列表 + * + * @return 用户表列表 + */ + List list(); + + /** + * 根据用户表id获取用户表 + * + * @param userId 用户表id + * @return 用户表 + */ + UserApiVO getByUserId(@Param("userId") Long userId); + + /** + * 保存用户表 + * + * @param user 用户表 + */ + void save(@Param("user") User user); + + /** + * 更新用户表 + * + * @param user 用户表 + */ + void update(@Param("user") User user); + + /** + * 根据用户id列表,获取用户信息 + * + * @param userIds + * @return + */ + List getUserByUserIds(@Param("userIds") List userIds); + + /** + * 根据用户id获取用户详细信息 + * @param userId 用户id + * @return 用户详细信息 + */ + UserApiVO getUserAndOpenIdsByUserId(@Param("userId") Long userId); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/Area.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/Area.java new file mode 100644 index 00000000..4e8e565d --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/Area.java @@ -0,0 +1,79 @@ +package com.mall4j.cloud.user.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 省市区地区信息 + * + * @author YXF + * @date 2020-11-25 15:16:14 + */ +public class Area extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + /** + * + */ + private Long areaId; + + /** + * 地址 + */ + private String areaName; + + /** + * 上级地址 + */ + private Long parentId; + + /** + * 等级(从1开始) + */ + private Integer level; + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getAreaName() { + return areaName; + } + + public void setAreaName(String areaName) { + this.areaName = areaName; + } + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + @Override + public String toString() { + return "Area{" + + "areaId=" + areaId + + ",areaName=" + areaName + + ",parentId=" + parentId + + ",level=" + level + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/User.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/User.java new file mode 100644 index 00000000..1d03f9ce --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/User.java @@ -0,0 +1,80 @@ +package com.mall4j.cloud.user.model; + +import java.io.Serializable; + +import com.mall4j.cloud.common.model.BaseModel; +/** + * 用户表 + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public class User extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + + public static final String DISTRIBUTED_ID_KEY = "mall4cloud-user"; + + /** + * ID + */ + private Long userId; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 头像图片路径 + */ + private String pic; + + /** + * 状态 1 正常 0 无效 + */ + private Integer status; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "User{" + + "userId=" + userId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",nickName=" + nickName + + ",pic=" + pic + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/UserAddr.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/UserAddr.java new file mode 100644 index 00000000..f28b2abb --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/model/UserAddr.java @@ -0,0 +1,235 @@ +package com.mall4j.cloud.user.model; + +import com.mall4j.cloud.common.model.BaseModel; + +import java.io.Serializable; + +/** + * 用户地址 + * + * @author FrozenWatermelon + * @date 2020-12-07 15:50:02 + */ +public class UserAddr extends BaseModel implements Serializable{ + private static final long serialVersionUID = 1L; + public static final Integer DEFAULT_ADDR = 1; + public static final Integer NOT_DEFAULT_ADDR = 0; + + /** + * ID + */ + private Long addrId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 手机 + */ + private String mobile; + + /** + * 是否默认地址 1是 + */ + private Integer isDefault; + + /** + * 收货人 + */ + private String consignee; + + /** + * 省ID + */ + private Long provinceId; + + /** + * 省 + */ + private String province; + + /** + * 城市ID + */ + private Long cityId; + + /** + * 城市 + */ + private String city; + + /** + * 区ID + */ + private Long areaId; + + /** + * 区 + */ + private String area; + + /** + * 邮编 + */ + private String postCode; + + /** + * 地址 + */ + private String addr; + + /** + * 经度 + */ + private Double lng; + + /** + * 纬度 + */ + private Double lat; + + public Long getAddrId() { + return addrId; + } + + public void setAddrId(Long addrId) { + this.addrId = addrId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getIsDefault() { + return isDefault; + } + + public void setIsDefault(Integer isDefault) { + this.isDefault = isDefault; + } + + public String getConsignee() { + return consignee; + } + + public void setConsignee(String consignee) { + this.consignee = consignee; + } + + public Long getProvinceId() { + return provinceId; + } + + public void setProvinceId(Long provinceId) { + this.provinceId = provinceId; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public Long getCityId() { + return cityId; + } + + public void setCityId(Long cityId) { + this.cityId = cityId; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public Long getAreaId() { + return areaId; + } + + public void setAreaId(Long areaId) { + this.areaId = areaId; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getPostCode() { + return postCode; + } + + public void setPostCode(String postCode) { + this.postCode = postCode; + } + + public String getAddr() { + return addr; + } + + public void setAddr(String addr) { + this.addr = addr; + } + + public Double getLng() { + return lng; + } + + public void setLng(Double lng) { + this.lng = lng; + } + + public Double getLat() { + return lat; + } + + public void setLat(Double lat) { + this.lat = lat; + } + + @Override + public String toString() { + return "UserAddr{" + + "addrId=" + addrId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",userId=" + userId + + ",mobile=" + mobile + + ",isDefault=" + isDefault + + ",consignee=" + consignee + + ",provinceId=" + provinceId + + ",province=" + province + + ",cityId=" + cityId + + ",city=" + city + + ",areaId=" + areaId + + ",area=" + area + + ",postCode=" + postCode + + ",addr=" + addr + + ",lng=" + lng + + ",lat=" + lat + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/AreaService.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/AreaService.java new file mode 100644 index 00000000..333f8592 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/AreaService.java @@ -0,0 +1,78 @@ +package com.mall4j.cloud.user.service; + +import com.mall4j.cloud.api.user.vo.AreaVO; +import com.mall4j.cloud.user.model.Area; + +import java.util.List; + +/** + * 省市区地区信息 + * + * @author YXF + * @date 2020-11-25 14:48:52 + */ +public interface AreaService { + + /** + * 获取地址列表 + * @return + */ + List list(); + + /** + * 根据省市区地区信息id获取省市区地区信息 + * + * @param areaId 省市区地区信息id + * @return 省市区地区信息 + */ + AreaVO getByAreaId(Long areaId); + + /** + * 保存省市区地区信息 + * @param area 省市区地区信息 + */ + void save(Area area); + + /** + * 更新省市区地区信息 + * @param area 省市区地区信息 + */ + void update(Area area); + + /** + * 根据省市区地区信息id删除省市区地区信息 + * @param areaId + */ + void deleteById(Long areaId); + + /** + * 根据上级地址id,获取地址列表 + * @param pid + * @return + */ + List listByPid(Long pid); + + /** + * 清除地址的所有缓存数据 + * @param pid + */ + void removeAllCache(Long pid); + + /** + * 根据上级分类id,清除缓存 + * @param pid + */ + void removeAreaCacheByParentId(Long pid); + /** + * 获取省市区三级结构完整的集合 + * + * @return 省市区三级结构完整的集合 + */ + List getAreaListInfo(); + + /** + * 获取可用的省市区列表 + * @return + */ + List listAreaOfEnable(); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserAddrService.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserAddrService.java new file mode 100644 index 00000000..bb1b31ff --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserAddrService.java @@ -0,0 +1,61 @@ +package com.mall4j.cloud.user.service; + +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.user.model.UserAddr; + +import java.util.List; + +/** + * @Author lth + * @Date 2021/7/1 17:37 + */ +public interface UserAddrService { + + /** + * 获取用户地址列表 + * @param userId + * @return + */ + List list(Long userId); + + /** + * 保存用户地址 + * @param userAddr 用户地址 + */ + void save(UserAddr userAddr); + + /** + * 更新用户地址 + * @param userAddr 用户地址 + */ + void update(UserAddr userAddr); + + /** + * 删除地址 + * @param addrId 地址id + * @param userId 用户id + */ + void deleteUserAddrByUserId(Long addrId, Long userId); + + /** + * 根据用户地址id和用户id获取用户地址信息 + * @param addrId 地址id + * @param userId 用户id + * @return 用户地址信息 + */ + UserAddrVO getUserAddrByUserId(Long addrId, Long userId); + + /** + * 用户地址的数量 + * @param userId 用户id + * @return 数量 + */ + int countByUserId(Long userId); + + + /** + * 移除用户默认地址的缓存 + * @param userId 用户id + */ + void removeUserDefaultAddrCacheByUserId(Long userId); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserService.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserService.java new file mode 100644 index 00000000..ea43fdaf --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/UserService.java @@ -0,0 +1,60 @@ +package com.mall4j.cloud.user.service; + +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.user.dto.UserRegisterDTO; +import com.mall4j.cloud.user.model.User; + +import java.util.List; + +/** + * 用户表 + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public interface UserService { + + /** + * 分页获取用户表列表 + * @param pageDTO 分页参数 + * @return 用户表列表分页数据 + */ + PageVO page(PageDTO pageDTO); + + /** + * 根据用户表id获取用户表 + * + * @param userId 用户表id + * @return 用户表 + */ + UserApiVO getByUserId(Long userId); + + /** + * 更新用户表 + * @param user 用户表 + */ + void update(User user); + + /** + * 根据用户id列表,获取用户信息 + * @param userIds + * @return + */ + List getUserByUserIds(List userIds); + + /** + * 根据用户id获取用户详细信息 + * @param userId 用户id + * @return 用户详细信息 + */ + UserApiVO getUserAndOpenIdsByUserId(Long userId); + + /** + * 保存用户 + * @param param 注册信息 + * @return uid + */ + Long save(UserRegisterDTO param); +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/AreaServiceImpl.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/AreaServiceImpl.java new file mode 100644 index 00000000..f0a6c55c --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/AreaServiceImpl.java @@ -0,0 +1,107 @@ +package com.mall4j.cloud.user.service.impl; + +import com.google.common.collect.Lists; +import com.mall4j.cloud.api.user.vo.AreaVO; +import com.mall4j.cloud.common.cache.constant.CacheNames; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.user.mapper.AreaMapper; +import com.mall4j.cloud.user.model.Area; +import com.mall4j.cloud.user.service.AreaService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 省市区地区信息 + * + * @author YXF + * @date 2020-11-25 14:48:52 + */ +@Service +public class AreaServiceImpl implements AreaService { + + @Autowired + private AreaMapper areaMapper; + + @Override + public List list() { + return areaMapper.list(); + } + + @Override + @Cacheable(cacheNames = CacheNames.AREA_INFO_KEY, key = "'areaList'", sync = true) + public List getAreaListInfo() { + List areaList = areaMapper.getAreaListInfo(); + for (AreaVO province:areaList){ + List cityAll = Lists.newArrayList(); + for (AreaVO city:province.getAreas()){ + cityAll.add(city.getAreaId()); + List areaAll = Lists.newArrayList(); + for (AreaVO area:city.getAreas()){ + areaAll.add(area.getAreaId()); + } + city.setAreaIds(areaAll); + } + province.setAreaIds(cityAll); + } + return areaList; + } + + @Override + public List listAreaOfEnable() { + List list = areaMapper.listAreaOfEnable(); + return list; + } + + @Override + public AreaVO getByAreaId(Long areaId) { + return areaMapper.getByAreaId(areaId); + } + + @Override + public void save(Area area) { + areaMapper.save(area); + } + + @Override + public void update(Area area) { + areaMapper.update(area); + } + + @Override + public void deleteById(Long areaId) { + int areaNum = areaMapper.countByAreaId(areaId); + if (areaNum > 0) { + throw new mall4cloudException("请先删除子地区"); + } + areaMapper.deleteById(areaId); + } + + @Override + @Cacheable(cacheNames = CacheNames.AREA_KEY, key = "'list:' + #pid") + public List listByPid(Long pid) { + return areaMapper.listByPid(pid); + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.AREA_INFO_KEY, key = "'areaList'"), + @CacheEvict(cacheNames = CacheNames.AREA_KEY, key = "'list:' + #pid") + }) + public void removeAllCache(Long pid) { + + } + + @Override + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.AREA_KEY, key = "'list:' + #pid"), + @CacheEvict(cacheNames = CacheNames.AREA_INFO_KEY, key = "'areaList'") + }) + public void removeAreaCacheByParentId(Long pid) { + + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserAddrServiceImpl.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserAddrServiceImpl.java new file mode 100644 index 00000000..081be4f3 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserAddrServiceImpl.java @@ -0,0 +1,72 @@ +package com.mall4j.cloud.user.service.impl; + +import com.mall4j.cloud.common.cache.constant.UserCacheNames; +import com.mall4j.cloud.common.order.vo.UserAddrVO; +import com.mall4j.cloud.user.mapper.UserAddrMapper; +import com.mall4j.cloud.user.model.UserAddr; +import com.mall4j.cloud.user.service.UserAddrService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * @Author lth + * @Date 2021/7/1 17:38 + */ +@Service +public class UserAddrServiceImpl implements UserAddrService { + + @Autowired + private UserAddrMapper userAddrMapper; + + @Override + public List list(Long userId) { + return userAddrMapper.list(userId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(UserAddr userAddr) { + if (userAddr.getIsDefault().equals(UserAddr.DEFAULT_ADDR)) { + userAddrMapper.removeDefaultUserAddr(userAddr.getUserId()); + } + userAddrMapper.save(userAddr); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(UserAddr userAddr) { + if (userAddr.getIsDefault().equals(UserAddr.DEFAULT_ADDR)) { + userAddrMapper.removeDefaultUserAddr(userAddr.getUserId()); + } + userAddrMapper.update(userAddr); + } + + @Override + public void deleteUserAddrByUserId(Long addrId, Long userId) { + userAddrMapper.deleteById(addrId,userId); + } + + @Override + public UserAddrVO getUserAddrByUserId(Long addrId, Long userId) { + // 获取用户默认地址 + if (addrId == 0) { + return userAddrMapper.getUserDefaultAddrByUserId(userId); + } + return userAddrMapper.getByAddrId(addrId,userId); + } + + @Override + public int countByUserId(Long userId) { + return userAddrMapper.countByUserId(userId); + } + + @Override + @CacheEvict(cacheNames = UserCacheNames.USER_DEFAULT_ADDR, key = "#userId") + public void removeUserDefaultAddrCacheByUserId(Long userId) { + + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserServiceImpl.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserServiceImpl.java new file mode 100644 index 00000000..10abc25a --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/service/impl/UserServiceImpl.java @@ -0,0 +1,134 @@ +package com.mall4j.cloud.user.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mall4j.cloud.api.auth.constant.SysTypeEnum; +import com.mall4j.cloud.api.auth.dto.AuthAccountDTO; +import com.mall4j.cloud.api.auth.feign.AccountFeignClient; +import com.mall4j.cloud.api.auth.vo.AuthAccountVO; +import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient; +import com.mall4j.cloud.api.user.vo.UserApiVO; +import com.mall4j.cloud.common.cache.constant.UserCacheNames; +import com.mall4j.cloud.common.database.dto.PageDTO; +import com.mall4j.cloud.common.database.util.PageUtil; +import com.mall4j.cloud.common.database.vo.PageVO; +import com.mall4j.cloud.common.exception.mall4cloudException; +import com.mall4j.cloud.common.response.ResponseEnum; +import com.mall4j.cloud.common.response.ServerResponseEntity; +import com.mall4j.cloud.common.util.IpHelper; +import com.mall4j.cloud.user.dto.UserRegisterDTO; +import com.mall4j.cloud.user.model.User; +import com.mall4j.cloud.user.mapper.UserMapper; +import com.mall4j.cloud.user.service.UserService; +import io.seata.spring.annotation.GlobalTransactional; +import ma.glasnost.orika.MapperFacade; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * 用户表 + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserMapper userMapper; + @Autowired + private AccountFeignClient accountFeignClient; + @Autowired + private MapperFacade mapperFacade; + @Autowired + private SegmentFeignClient segmentFeignClient; + + @Override + public PageVO page(PageDTO pageDTO) { + return PageUtil.doPage(pageDTO, () -> userMapper.list()); + } + + @Override + @Cacheable(cacheNames = UserCacheNames.USER_INFO, key = "#userId") + public UserApiVO getByUserId(Long userId) { + return userMapper.getByUserId(userId); + } + + @Override + @CacheEvict(cacheNames = UserCacheNames.USER_INFO, key = "#user.userId") + public void update(User user) { + userMapper.update(user); + } + + @Override + public List getUserByUserIds(List userIds) { + if (CollUtil.isEmpty(userIds)) { + return new ArrayList<>(); + } + return userMapper.getUserByUserIds(userIds); + } + + @Override + public UserApiVO getUserAndOpenIdsByUserId(Long userId) { + return userMapper.getUserAndOpenIdsByUserId(userId); + } + + @Override + @GlobalTransactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) + public Long save(UserRegisterDTO param) { + this.checkRegisterInfo(param); + + ServerResponseEntity segmentIdResponse = segmentFeignClient.getSegmentId(User.DISTRIBUTED_ID_KEY); + if (!segmentIdResponse.isSuccess()) { + throw new mall4cloudException(ResponseEnum.EXCEPTION); + } + Long userId = segmentIdResponse.getData(); + + param.setUserId(userId); + + AuthAccountDTO authAccountDTO = new AuthAccountDTO(); + authAccountDTO.setCreateIp(IpHelper.getIpAddr()); + authAccountDTO.setPassword(param.getPassword()); + authAccountDTO.setIsAdmin(0); + authAccountDTO.setSysType(SysTypeEnum.ORDINARY.value()); + authAccountDTO.setUsername(param.getUserName()); + authAccountDTO.setStatus(1); + authAccountDTO.setUserId(userId); + + // 保存统一账户信息 + ServerResponseEntity serverResponse = accountFeignClient.save(authAccountDTO); + // 抛异常回滚 + if (!serverResponse.isSuccess()) { + throw new mall4cloudException(serverResponse.getMsg()); + } + + User user = new User(); + user.setUserId(userId); + user.setPic(param.getImg()); + user.setNickName(param.getNickName()); + user.setStatus(1); + // 这里保存之后才有用户id + userMapper.save(user); + + return serverResponse.getData(); + } + + private void checkRegisterInfo(UserRegisterDTO userRegisterDTO) { + ServerResponseEntity responseEntity = accountFeignClient.getByUsernameAndSysType(userRegisterDTO.getUserName(), SysTypeEnum.ORDINARY); + if (!responseEntity.isSuccess()) { + throw new mall4cloudException(responseEntity.getMsg()); + } + if (Objects.nonNull(responseEntity.getData())) { + throw new mall4cloudException("用户名已存在"); + } + } + +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserSimpleInfoVO.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserSimpleInfoVO.java new file mode 100644 index 00000000..eb13ae6f --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserSimpleInfoVO.java @@ -0,0 +1,40 @@ +package com.mall4j.cloud.user.vo; + +import io.swagger.annotations.ApiModelProperty; + +/** + * @author FrozenWatermelon + * @date 2021/2/25 + */ +public class UserSimpleInfoVO { + + @ApiModelProperty(value = "用户昵称",required=true) + private String nickName; + + @ApiModelProperty(value = "用户头像",required=true) + private String pic; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + @Override + public String toString() { + return "UserCenterInfoVO{" + + "nickName='" + nickName + '\'' + + ", pic='" + pic + '\'' + + '}'; + } +} diff --git a/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserVO.java b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserVO.java new file mode 100644 index 00000000..98265950 --- /dev/null +++ b/mall4cloud-user/src/main/java/com/mall4j/cloud/user/vo/UserVO.java @@ -0,0 +1,85 @@ +package com.mall4j.cloud.user.vo; + +import com.mall4j.cloud.common.vo.BaseVO; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * 用户表VO + * + * @author YXF + * @date 2020-12-08 11:18:04 + */ +public class UserVO extends BaseVO{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + private Long userId; + + @ApiModelProperty("用户昵称") + private String nickName; + + @ApiModelProperty("头像图片路径") + private String pic; + + @ApiModelProperty("状态 1 正常 0 无效") + private Integer status; + + /** + * openId list + */ + private List bizUserIdList; + + public List getBizUserIdList() { + return bizUserIdList; + } + + public void setBizUserIdList(List bizUserIdList) { + this.bizUserIdList = bizUserIdList; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPic() { + return pic; + } + + public void setPic(String pic) { + this.pic = pic; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "UserApiVO{" + + "userId=" + userId + + ",createTime=" + createTime + + ",updateTime=" + updateTime + + ",nickName=" + nickName + + ",pic=" + pic + + ",status=" + status + + '}'; + } +} diff --git a/mall4cloud-user/src/main/resources/bootstrap.yml b/mall4cloud-user/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..52bff483 --- /dev/null +++ b/mall4cloud-user/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +server: + port: 9105 +spring: + application: + name: @artifactId@ + cloud: + nacos: + discovery: + server-addr: ${NACOS_HOST:192.168.1.46}:${NACOS_PORT:8848} + username: nacos + password: nacos + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: yml + namespace: @nacos.namespace@ + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + username: ${spring.cloud.nacos.discovery.username} + password: ${spring.cloud.nacos.discovery.password} + profiles: + active: @profiles.active@ diff --git a/mall4cloud-user/src/main/resources/mapper/AreaMapper.xml b/mall4cloud-user/src/main/resources/mapper/AreaMapper.xml new file mode 100644 index 00000000..0c14dd1b --- /dev/null +++ b/mall4cloud-user/src/main/resources/mapper/AreaMapper.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `area_id`,`area_name`,`parent_id`,`level`,`create_time`,`update_time` + + + + + insert into area (`area_name`,`parent_id`,`level`) + values (#{area.areaName},#{area.parentId},#{area.level}); + + + update area + + + `area_name` = #{area.areaName}, + + + `parent_id` = #{area.parentId}, + + + `level` = #{area.level}, + + + where area_id = #{area.areaId} + + + delete from area where area_id = #{areaId} + + + + + + + + + diff --git a/mall4cloud-user/src/main/resources/mapper/UserAddrMapper.xml b/mall4cloud-user/src/main/resources/mapper/UserAddrMapper.xml new file mode 100644 index 00000000..99f2750b --- /dev/null +++ b/mall4cloud-user/src/main/resources/mapper/UserAddrMapper.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + `addr_id`, + `create_time`, + `update_time`, + `mobile`, + `is_default`, + `consignee`, + `province_id`, + `province`, + `city_id`, + `city`, + `area_id`, + `area`, + `post_code`, + `addr`, + `lng`, + `lat` + + + + + insert into user_addr (`user_id`, `mobile`, `is_default`, `consignee`, `province_id`, `province`, `city_id`, + `city`, `area_id`, `area`, `post_code`, `addr`, `lng`, `lat`) + values (#{userAddr.userId}, #{userAddr.mobile}, #{userAddr.isDefault}, #{userAddr.consignee}, + #{userAddr.provinceId}, #{userAddr.province}, #{userAddr.cityId}, #{userAddr.city}, #{userAddr.areaId}, + #{userAddr.area}, #{userAddr.postCode}, #{userAddr.addr}, #{userAddr.lng}, #{userAddr.lat}); + + + update user_addr + + + `user_id` = #{userAddr.userId}, + + + `mobile` = #{userAddr.mobile}, + + + `is_default` = #{userAddr.isDefault}, + + + `consignee` = #{userAddr.consignee}, + + + `province_id` = #{userAddr.provinceId}, + + + `province` = #{userAddr.province}, + + + `city_id` = #{userAddr.cityId}, + + + `city` = #{userAddr.city}, + + + `area_id` = #{userAddr.areaId}, + + + `area` = #{userAddr.area}, + + + `post_code` = #{userAddr.postCode}, + + + `addr` = #{userAddr.addr}, + + + `lng` = #{userAddr.lng}, + + + `lat` = #{userAddr.lat}, + + + where addr_id = #{userAddr.addrId} + + + delete + from user_addr + where addr_id = #{addrId} + and user_id = #{userId} + + + + update user_addr + set is_default = 0 + where user_id = #{userId} and is_default = 1 + + + + update user_addr + set is_default = 1 + where user_id = #{userId} + and addr_id = #{addrId} + + + + diff --git a/mall4cloud-user/src/main/resources/mapper/UserMapper.xml b/mall4cloud-user/src/main/resources/mapper/UserMapper.xml new file mode 100644 index 00000000..10c00053 --- /dev/null +++ b/mall4cloud-user/src/main/resources/mapper/UserMapper.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + `user_id`,`create_time`,`update_time`,`nick_name`,`pic`,`status` + + + + + + + + + insert into user (`user_id`,`nick_name`,`pic`,`status`) + values (#{user.userId},#{user.nickName},#{user.pic},#{user.status}); + + + update user + + + `nick_name` = #{user.nickName}, + + + `pic` = #{user.pic}, + + + `status` = #{user.status}, + + + where user_id = #{user.userId} + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..15e37dc9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,285 @@ + + + 4.0.0 + + com.mall4j.cloud + mall4cloud + 1.0-SNAPSHOT + + mall4cloud-gateway + mall4cloud-auth + mall4cloud-common + mall4cloud-rbac + mall4cloud-leaf + mall4cloud-api + mall4cloud-multishop + mall4cloud-platform + mall4cloud-biz + mall4cloud-product + mall4cloud-search + mall4cloud-user + mall4cloud-order + mall4cloud-payment + + pom + + + + UTF-8 + 3.8.1 + 3.1.0 + 1.8 + + + 2.4.2 + 2020.0.2 + 2021.1 + + + 1.4.2 + 2.0.1 + + + 5.5.8 + 2.1.4 + 1.3.0 + 1.13.1 + 1.5.4 + 1.2.5 + 3.0.2 + 7.9.1 + 8.0.3 + 2.2.0 + 4.0.0 + 0.0.22 + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + + com.google.guava + guava + + + + + io.seata + seata-spring-boot-starter + ${seata.version} + + + com.google.guava + guava + + + com.alibaba + druid + + + + + + cn.hutool + hutool-core + ${hutool-version} + + + cn.hutool + hutool-crypto + ${hutool-version} + + + cn.hutool + hutool-http + ${hutool-version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot-starter.version} + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper-spring-boot-starter.version} + + + org.jsoup + jsoup + ${jsoup.version} + + + ma.glasnost.orika + orika-core + ${orika.version} + + + com.github.anji-plus + captcha + ${captcha.version} + + + com.github.xiaoymin + knife4j-micro-spring-boot-starter + ${knife4j.version} + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + ${elasticsearch.version} + + + org.elasticsearch + elasticsearch + ${elasticsearch.version} + + + io.minio + minio + ${minio.version} + + + org.elasticsearch.client + elasticsearch-rest-client + ${elasticsearch.version} + + + commons-logging + commons-logging + + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-springboot.version} + + + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + ${project.name} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + ${spring-javaformat.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.encoding} + + + + maven-resources-plugin + ${maven-resources-plugin.version} + + ${project.encoding} + + + + + + src/main/resources + true + + + + + + + + dev + + + dev + + + + + + true + + + + test + + test + x + + + + prod + + prod + x + + + +