初始化项目

This commit is contained in:
2024-10-29 12:10:22 +08:00
commit 029195fbbc
847 changed files with 125861 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# Maven #
target/
# IDEA #
.idea/
*.iml
# Eclipse #
.settings/
.classpath
.project

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2018-2019] [macrozheng]
Licensed 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.

375
README-DEV.md Normal file
View File

@@ -0,0 +1,375 @@
# mall
## 技术选型
### 后端技术
技术 | 名称
----|----
Spring Boot | 容器+MVC框架
Spring Security | 认证和授权框架
MyBatis | ORM框架
MyBatisGenerator | 数据层代码生成
PageHelper | MyBatis物理分页插件
Swagger-UI | 文档生产工具
Hibernator-Validator | 验证框架
Elasticsearch | 搜索引擎
RabbitMq | 消息队列
Redis | 分布式缓存
MongoDb | NoSql数据库
Docker | 应用容器引擎
Druid | 数据库连接池
### 前端技术
技术 | 名称
----|----
Vue | 前端框架
Vue-router | 路由框架
Vuex | 全局状态管理框架
Element | 前端UI框架
Axios | 前端HTTP框架
Js-cookie | cookie管理工具
### 框架搭建
功能 | 完成
----|----
集成MyBatis | ✔
集成MyBatisGenerator | ✔
集成SpringSecurity | ✔
集成Swagger-UI | ✔
集成Hibernator-Validator | ✔
集成日志功能 | ✔
集成监控功能 | ✔
crud操作demo | ✔
合理规划包结构 | ✔
SpringAOP通用日志处理 | ✔
SpringAOP通用验证失败结果返回 | ✔
CommonResult对通用返回结果进行封装 | ✔
SpringSecurity登录改为Restful形式 | ✔
JWT登录、注册、获取token | ✔
JTA事务处理 | ✔
集成单元测试 | ✔
OSS上传功能 | ✔
Elasticsearch搜索功能 | ✔
HTTPS支持 | ✔
Redis数字型ID生成 | ✔
SpringTask定时任务支持 | ✔
docker容器化部署 | ✔
配置区分生产和测试环境 | ✔
ELK日志收集功能 | ✔
RabbitMq异步通信 | ✔
RestTemplate服务间调用 | ✔
SpringSecurity权限管理功能 | ✔
集成SpringCloud |
### 使用工具
工具 | 下载地址
----|----
开发工具idea | https://www.jetbrains.com/idea/download
redis客户端连接工具 | https://redisdesktop.com/download
mongo客户端连接工具 | https://robomongo.org/download
本地host管理 | https://oldj.github.io/SwitchHosts/
Linux远程连接工具 | http://www.netsarang.com/download/software.html
数据库连接工具 | http://www.formysql.com/xiazai.html
数据库设计工具 | http://powerdesigner.de/
原型设计工具 | https://www.axure.com/
思维导图设计工具 | http://www.edrawsoft.cn/mindmaster
gif录制工具 | https://www.screentogif.com/
### 后台功能
#### 后台登录功能 ✔
- 后台用户注册功能
- 后台用户登录后获取token
- 刷新token功能
#### 商品管理 ✔
> **商品分类管理**
- 按父分类编号分页查看分类列表
- 添加、编辑、删除分类
- 转移分类商品
> **商品品牌管理**
- 按品牌名称搜索分页查看品牌列表
- 添加、编辑、删除品牌
- 查看当前品牌的所有产品
> **商品属性分类管理**
- 添加商品属性分类(名称)
- 分页查询全部商品属性分类
- 删除单个商品属性分类
- 修改单个属性分类名称
- 查询单个属性分类信息
> **商品属性管理**
- 根据分类查询属性列表或参数列表(分页,支持类型)
- 添加商品属性
- 查询单个商品属性
- 编辑商品属性
- 批量删除商品属性
- 分页查询全部商品属性
> **添加商品**
- 选择商品分类根据商品分类id查找分类
- 选择品牌:查询全部品牌
- 选择运费模版:查询全部运费模版
- 设置会员价格查询所有会员等级传入List<PmsMemberPrice>
- 添加阶梯价格: 参数传入List<PmsProductLadder>
- 设置满减价格: 参数传入List<PmsProductFullReduction>
- 选择商品属性类别:获取所有商品属性分类根据商品属性分类的id获取规格和参数(type=0->规格type=1->参数)
- 选择规格并生成库存信息:前端实现
- 添加sku库存信息参数传入List<PmsSkuStock>
- 设置属性图片设置到pic和album_pics字段中去
- 添加商品参数参数传入List<PmsProductAttributeValue>
- 添加自定义商品规格参数传入List<PmsProductAttributeValue>
- 关联专题:参数传入List<CmsSubjectProductRelation>关系
- 关联优选:参数传入List<CmsPrefrenceAreaProductRelation>关系
> **修改商品**
- 根据商品id查询商品信息
- 查询商品基本信息:商品分类名称、品牌名称、运费模版名称
- 查询商品促销信息:商品的会员价格、阶梯价格、满减价格
- 查询商品属性信息商品属性类别名称、sku库存信息、属性分类对应规格和参数值
- 查询商品关联信息:商品关联专题和关联优选
- 修改商品信息商品属性分类及规格不可修改只支持单个sku的修改、删除、新增商品属性分类及规格可以修改修改后同时显示原sku库存及属性分类
> **商品分页查询**
- 商品的状态:全部商品、已上架、未上架、待审核、未通过 (publishStatus verifyStatus)
- 商品名称(%name%)
- 商品货号(productSn)
- 商品分类id(productCategoryId)
- 商品品牌id(brandId)
- 批量操作:上下架、推荐、新品、转移分类、放入回收站、审核
- 查看记录:审核记录,操作日志
- sku:根据产品及sku编号获取sku信息批量修改sku信息
> **商品回收管理**
- 分页展示回收商品列表
- 回收商品还原功能
#### 促销管理
> **秒杀活动管理**
- 活动列表展示
- 活动上下线
- 设置活动商品
- 添加、编辑、删除活动
> **优惠券管理**
- 优惠券列表展示
- 添加、编辑、删除优惠券
- 查看优惠券领取记录
> **活动管理**
- 活动列表展示
- 添加、编辑、删除活动
- 活动上下线
- 发布到广告
> **首页推荐**
- 品牌推荐:列表展示、是否推荐、排序、删除、多选加入品牌
- 新鲜好物:商品列表展示、是否推荐、排序、删除、多选加入商品
- 人气推荐:商品列表展示、是否推荐、排序、删除、多选加入商品
- 专题精选:专题列表展示、是否推荐、排序、删除、多选加入专题
- 广告管理:广告列表展示、是否上线、排序、删除、添加编辑广告
#### 内容管理
> **专题管理**
- 专题列表:查看、删除、推荐专题
- 添加、编辑专题:选择专题分类、添加、删除关联商品
- 专题分类管理:控制显示、排序、编辑删除分类
> **优选主题**
- 优选列表:控制显示、排序、删除
- 添加、编辑优选:关联和删除商品
> **话题管理**
- 专题列表:查看、删除、热门话题
- 话题分类管理:控制显示、排序、编辑删除分类
> **帮助管理**
- 帮助列表:查看、删除、控制显示
- 添加、编辑帮助:选择帮助分类
- 帮助分类管理:控制显示、排序、编辑删除分类
#### 用户管理
> **用户管理**
- 用户列表:帐号启用、删除、群发短信
- 批量操作:群发短信、站内信、推送、设置标签、赠送优惠券
- 查看、编辑用户信息:用户详情(统计信息、收货地址、订单记录)、编辑资料、登录日志
- 购买力筛选:最近消费、消费次数、消费金额、订单均价、商品分类、会员等级、用户标签
- 用户标签管理:标签列表、添加、编辑、删除
- 会员等级设置:列表、添加、编辑、设置默认会员等级
> **成才值及积分**
- 成长值及积分查询:列表展示、积分明细、成长值明细、修改数值
- 任务奖励设置:新手任务、日常任务
- 更多规则设置:成长值规则、积分规则、积分消费设置
#### 订单管理
> **订单列表**
- 订单搜索:订单编号、收货人姓名/号码、订单状态、订单分类、订单来源、提交时间
- 订单操作:查看订单、关闭订单、订单发货、订单跟踪、删除订单
- 批量操作:批量发货、关闭订单、删除订单
> **查看订单**
- 订单状态:提交订单、支付订单、平台发货、确认收货、完成评价
- 订单详情操作:修改发票信息、修改收货人信息、修改商品信息、修改费用信息、发送站内信、关闭订单、备注订单、取消订单、订单跟踪、删除订单
- 订单基本信息:订单信息、优惠信息、用户信息
- 发票信息:类型、抬头、内容、收票人信息
- 收货人信息:收货人、手机号、邮政编码、收货地址
- 商品信息:包含商品基本信息
- 费用信息:合计及优惠信息
- 操作信息:订单状态改变记录
> **订单设置**
- 秒杀订单超时时间
- 正常订单超时时间
- 订单自动完成时间
- 自动结束交易时间
- 自动好评时间
> **退货申请处理**
- 退货申请搜索:服务单号、收货人姓名/号码、处理状态、申请时间、操作人员、处理时间
- 批量操作:删除
- 查看详情:退货商品、服务单信息、确认退货、拒绝退货、确认收货
> **退货原因设置**
- 原因列表:类型、排序、是否启用、添加时间
- 添加、编辑原因:同上
- 批量操作:删除
#### 权限管理 ✔
> **权限管理**
- 权限管理:添加权限、删除权限、修改权限、以树形结构返回权限
- 角色管理:添加角色、删除角色、更新角色、角色列表、获取相应角色权限、修改相应角色权限
- 成员管理:添加、编辑、删除成员、成员列表、为成员分配角色、获取成员角色、分配+-权限、获取权限列表
角色 | 菜单
----|----
管理员 | 所有菜单权限
运营 | 首页、用户、促销、运营、内容
财务 | 首页、统计、财务
美工 | 首页、商品
客服 | 首页、商品、订单
### 前台功能
#### 商品搜索 ✔
> **综合搜索功能**
- 搜索:根据商品标题、副标题、关键字进行搜索;
- 筛选:未选择分类时聚合搜索结果,选择出现次数最多的分类,选择分类以后可以根据选择分类的筛选字段进行筛选;
- 排序:按新品、销量、价格进行排序
- 搜索返回结果商品ID、商品图片、名称、副标题、价格、商品销量、新品、商品的参数、品牌名称、分类名称
- 接口从数据库中查询相关数据并导入es,插入(修改)数据接口,删除数据接口
- 品牌分类筛选:根据搜索结果聚合返回品牌、分类及属性
> **商品推荐功能**
- 推荐某商品的相关商品、根据该商品的品牌10、分类6、名称8、关键字2、副标题2
- 根据用户一周浏览记录推荐商品,根据用户搜索记录推荐商品
> **商品热搜功能**
- 根据用户搜索记录聚合生成热搜词
> **商品搜索联想功能**
- 根据用户搜索记录聚合生成热搜词
#### 购物流程 ✔
> **购物车**
- 添加商品到购物车
- 购物车商品列表(商品主图、商品名称、商品数量、商品规格)
- 修改购物车中商品数量
- 购物车中商品重选规格
- 购物车中商品删除功能
> **生成确认单**
- 生成确认单信息:收货信息、商品信息、价格信息、发票信息、支付方式
- 选择收货地址:默认收货地址
- 选择优惠券及积分抵扣:展示可用优惠券和不可以优惠券
- 计算商品价格:商品合计、运费、优惠券抵扣、积分抵扣、活动优惠
- 选择支付方式:在线支付及货到付款
- 计算应付金额:合计+运费-优惠券抵扣-积分抵扣-活动优惠
> **提交订单**
- 将确认单信息转化为订单
- 删除购物车中相关商品
- 添加订单到数据库并锁定库存
- 在线支付选择支付方式支付宝、微信、银联、ApplePay
> **支付订单**
- 支付完成后修改订单状态为已支付、扣除库存
> **取消订单(未支付情况下)**
- 订单超时后自动取消订单,解除商品库存锁定,返还优惠券,积分
#### 会员模块(我的)✔
> **我的关注**
- 关注品牌列表品牌logo、名称、地址、关注数量
- 取消关注功能
- 关注列表展示
> **我的收藏**
- 收藏的商品:商品主图、名称、卖点、价格、找相似
- 收藏的专题:专题主图、标题、副标题、收藏数、浏览数、评论数
- 收藏的话题:话题主图、标题、副标题、收藏数、浏览数、评论数
> **我的足迹**
- 浏览过的商品:商品主图、名称、卖点、价格、找相似
- 删除记录功能
- 浏览列表展示
> **会员登录注册**
- 登录功能https登录
- 注册功能:用户名、密码、手机号、手机验证码
- 获取验证码:后台生成验证码,验证码绑定手机号
- 忘记密码:手机号、短信验证码、新密码
- 登出功能

211
README.md Normal file
View File

@@ -0,0 +1,211 @@
# mall
<p>
<a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-macrozheng-blue.svg" alt="公众号"></a>
<a href="https://github.com/macrozheng/mall-learning"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%AD%A6%E4%B9%A0%E6%95%99%E7%A8%8B-mall--learning-green.svg" alt="学习教程"></a>
<a href="http://qm.qq.com/cgi-bin/qm/qr?k=V6xu5c12j9qhnMUNdDRzakNxRKzOxibQ"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/QQ%E7%BE%A4-959351312-red.svg" alt="QQ群"></a>
<a href="http://qm.qq.com/cgi-bin/qm/qr?k=M5Edq2TiJL_ShcOEeYjwcmdGmq4zZrd_"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/QQ%E7%BE%A4-553018255-red.svg" alt="QQ群"></a>
<a href="https://gitee.com/macrozheng/mall"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E7%A0%81%E4%BA%91-%E9%A1%B9%E7%9B%AE%E5%9C%B0%E5%9D%80-orange.svg" alt="码云"></a>
</p>
## 前言
`mall`项目致力于打造一个完整的电商系统,采用现阶段流行技术实现。
## 特别赞助商
<p align="left">
<a href="https://coding.net/?utm_source=macrozheng&utm_medium=banner&utm_campaign=march2019" target="_blank">
<img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/coding.png" width=""/>
</a>
</p>
## 项目介绍
`mall`项目是一套电商系统包括前台商城系统及后台管理系统基于SpringBoot+MyBatis实现。
前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。
后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
### 项目演示
#### 后台管理系统
前端项目`mall-admin-web`地址https://github.com/macrozheng/mall-admin-web
项目演示地址: [http://39.98.190.128/index.html](http://39.98.190.128/index.html)
![后台管理系统功能演示.gif](/document/resource/mall-admin.gif)
#### 前台商城系统
前端项目`mall-app-web`地址:敬请期待......
项目演示地址:[http://39.98.190.128/mall-app/mainpage.html](http://39.98.190.128/mall-app/mainpage.html)
![前台商城系统功能演示.gif](/document/resource/mall-app.gif)
### 组织结构
``` lua
mall
├── mall-common -- 工具类及通用代码
├── mall-mbg -- MyBatisGenerator生成的数据库操作代码
├── mall-admin -- 后台商城管理系统接口
├── mall-search -- 基于Elasticsearch的商品搜索系统
├── mall-portal -- 前台商城系统接口
└── mall-demo -- 框架搭建时的测试代码
```
### 技术选型
#### 后端技术
技术 | 说明 | 官网
----|----|----
Spring Boot | 容器+MVC框架 | [https://spring.io/projects/spring-boot](https://spring.io/projects/spring-boot)
Spring Security | 认证和授权框架 | [https://spring.io/projects/spring-security](https://spring.io/projects/spring-security)
MyBatis | ORM框架 | [http://www.mybatis.org/mybatis-3/zh/index.html](http://www.mybatis.org/mybatis-3/zh/index.html)
MyBatisGenerator | 数据层代码生成 | [http://www.mybatis.org/generator/index.html](http://www.mybatis.org/generator/index.html)
PageHelper | MyBatis物理分页插件 | [http://git.oschina.net/free/Mybatis_PageHelper](http://git.oschina.net/free/Mybatis_PageHelper)
Swagger-UI | 文档生产工具 | [https://github.com/swagger-api/swagger-ui](https://github.com/swagger-api/swagger-ui)
Hibernator-Validator | 验证框架 | [http://hibernate.org/validator/](http://hibernate.org/validator/)
Elasticsearch | 搜索引擎 | [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
RabbitMq | 消息队列 | [https://www.rabbitmq.com/](https://www.rabbitmq.com/)
Redis | 分布式缓存 | [https://redis.io/](https://redis.io/)
MongoDb | NoSql数据库 | [https://www.mongodb.com/](https://www.mongodb.com/)
Docker | 应用容器引擎 | [https://www.docker.com/](https://www.docker.com/)
Druid | 数据库连接池 | [https://github.com/alibaba/druid](https://github.com/alibaba/druid)
OSS | 对象存储 | [https://github.com/aliyun/aliyun-oss-java-sdk](https://github.com/aliyun/aliyun-oss-java-sdk)
JWT | JWT登录支持 | [https://github.com/jwtk/jjwt](https://github.com/jwtk/jjwt)
LogStash | 日志收集 | [https://github.com/logstash/logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder)
Lombok | 简化对象封装工具 | [https://github.com/rzwitserloot/lombok](https://github.com/rzwitserloot/lombok)
#### 前端技术
技术 | 说明 | 官网
----|----|----
Vue | 前端框架 | [https://vuejs.org/](https://vuejs.org/)
Vue-router | 路由框架 | [https://router.vuejs.org/](https://router.vuejs.org/)
Vuex | 全局状态管理框架 | [https://vuex.vuejs.org/](https://vuex.vuejs.org/)
Element | 前端UI框架 | [https://element.eleme.io/](https://element.eleme.io/)
Axios | 前端HTTP框架 | [https://github.com/axios/axios](https://github.com/axios/axios)
v-charts | 基于Echarts的图表框架 | [https://v-charts.js.org/](https://v-charts.js.org/)
Js-cookie | cookie管理工具 | [https://github.com/js-cookie/js-cookie](https://github.com/js-cookie/js-cookie)
nprogress | 进度条控件 | [https://github.com/rstacruz/nprogress](https://github.com/rstacruz/nprogress)
#### 架构图
##### 系统架构图
![系统架构图](document/resource/mall_system_arch.png)
##### 业务架构图
![系统架构图](document/resource/mall_business_arch.png)
#### 模块介绍
##### 后台管理系统 `mall-admin`
- 商品管理:[功能结构图-商品.jpg](document/resource/mind_product.jpg)
- 订单管理:[功能结构图-订单.jpg](document/resource/mind_order.jpg)
- 促销管理:[功能结构图-促销.jpg](document/resource/mind_sale.jpg)
- 内容管理:[功能结构图-内容.jpg](document/resource/mind_content.jpg)
- 用户管理:[功能结构图-用户.jpg](document/resource/mind_member.jpg)
##### 前台商城系统 `mall-portal`
[功能结构图-前台.jpg](document/resource/mind_portal.jpg)
#### 开发进度
![项目开发进度图](document/resource/mall_dev_flow.png)
## 环境搭建
### 开发工具
工具 | 说明 | 官网
----|----|----
IDEA | 开发IDE | https://www.jetbrains.com/idea/download
RedisDesktop | redis客户端连接工具 | https://redisdesktop.com/download
Robomongo | mongo客户端连接工具 | https://robomongo.org/download
SwitchHosts| 本地host管理 | https://oldj.github.io/SwitchHosts/
X-shell | Linux远程连接工具 | http://www.netsarang.com/download/software.html
Navicat | 数据库连接工具 | http://www.formysql.com/xiazai.html
PowerDesigner | 数据库设计工具 | http://powerdesigner.de/
Axure | 原型设计工具 | https://www.axure.com/
MindMaster | 思维导图设计工具 | http://www.edrawsoft.cn/mindmaster
ScreenToGif | gif录制工具 | https://www.screentogif.com/
ProcessOn | 流程图绘制工具 | https://www.processon.com/
PicPick | 屏幕取色工具 | https://picpick.app/zh/
### 开发环境
工具 | 版本号 | 下载
----|----|----
JDK | 1.8 | https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Mysql | 5.7 | https://www.mysql.com/
Redis | 3.2 | https://redis.io/download
Elasticsearch | 6.2.2 | https://www.elastic.co/downloads
MongoDb | 3.2 | https://www.mongodb.com/download-center
RabbitMq | 3.7.14 | http://www.rabbitmq.com/download.html
nginx | 1.10 | http://nginx.org/en/download.html
### 搭建步骤
> Windows环境部署
- Windows环境搭建请参考[mall在Windows环境下的部署](https://mp.weixin.qq.com/s/Q9ybpfq8IEdbZmvlaMXJdg);
- 安装Windows环境下的所有工具并启动(只启动mall-admin,仅需安装mysql);
- 克隆源代码到本地使用IDEA或Eclipse打开并完成编译;
- 在mysql中新建mall数据库导入document/sql下的mall.sql文件
- 启动mall-admin项目直接运行com.macro.mall.MallAdminApplication的main方法即可
接口文档地址http://localhost:8080/swagger-ui.html;
- 启动mall-search项目直接运行com.macro.mall.search.MallSearchApplication的main方法即可
接口文档地址http://localhost:8081/swagger-ui.html;
- 启动mall-portal项目直接运行com.macro.mall.portal.MallPortalApplication的main方法即可
接口文档地址http://localhost:8085/swagger-ui.html;
- 克隆`mall-admin-web`项目并导入到IDEA中完成编译[传送门](https://github.com/macrozheng/mall-admin-web);
- 在IDEA命令行中运行命令npm install,下载相关依赖;
- 在IDEA命令行中运行命令npm run dev,访问地址:[http://localhost:8090](http://localhost:8090) 即可打开后台管理系统页面;
- ELK日志收集系统的搭建参考[elk.md](document/elk/elk.md)。
> Docker环境部署
- 在VirtualBox或其他环境中安装CenterOs7.6;
- Docker环境的安装请参考:[开发者必备Docker命令](https://mp.weixin.qq.com/s/d_CuljDTJq680NTndAay8g)
- 本项目Docker镜像构建请参考:[使用Maven插件构建Docker镜像](https://mp.weixin.qq.com/s/q2KDzHbPkf3Q0EY8qYjYgw)
- 本项目在Docker容器下的部署请参考[mall在Linux环境下的部署基于Docker容器](https://mp.weixin.qq.com/s/0fVMK107i5bBq8kGQqg8KA)。
## 项目相关文档
具体详见`mall-learning`:[https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning)
## 参考资料
- [Spring实战第4版](https://book.douban.com/subject/26767354/)
- [Spring Boot实战](https://book.douban.com/subject/26857423/)
- [Spring Cloud微服务实战](https://book.douban.com/subject/27025912/)
- [Spring Cloud与Docker微服务架构实战](https://book.douban.com/subject/27028228/)
- [Spring Data实战](https://book.douban.com/subject/25975186/)
- [MyBatis从入门到精通](https://book.douban.com/subject/27074809/)
- [深入浅出MySQL](https://book.douban.com/subject/25817684/)
- [循序渐进Linux第2版](https://book.douban.com/subject/26758194/)
- [Elasticsearch 权威指南](https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html)
- [Elasticsearch 技术解析与实战](https://book.douban.com/subject/26967826/)
- [MongoDB实战(第二版)](https://book.douban.com/subject/27061123/)
- [Kubernetes权威指南](https://book.douban.com/subject/26902153/)
- [Pro Git](https://git-scm.com/book/zh/v2)
## 公众号
mall项目全套学习教程连载中**关注公众号**第一时间获取。
![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg)
## 许可证
[Apache License 2.0](https://github.com/macrozheng/mall/blob/master/LICENSE)
Copyright (c) 2018-2019 macrozheng

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAgVvtzKTrCPUwQEuwHVC6dselz2mpUbmxg8Z+S4gz3Ff2aeOn
QsZxoPYPoYneiMeS7uRGhlJnXdeT4dbqgZzbcMj+erDN51iIR72suUnXpDbHI3CJ
V5It4mGoq883U5RQCVDmqTs8pT4SoNxx5/kSp6cJYbLnVwsmV/8crLSKFitAXlaq
F01pRAn3FKDFMbytoD8kYoyxbmfC/M2mrvndoWTzwR8/qplgx7gjSLF2PAABg6Ao
ILGHx25Hvw6tRD2BRE7yUN/q6I8xe3/0LuCgwZ8iE8FlMvxAKPG651UBOloksu8H
Kf71ctJj6/wcdOB3zDYFd5kxjw36TwDAAG2QIwIDAQABAoIBAH58ty9NawydQwr7
dz+S+mC+oyEGfiuNWUgyxLdyfNNNp4KuuZQPmnf7YhlgSBqSmtfAmj0pQi1+Z/mC
PldyWxgTUNIDI7K83LTZgJTE4XiOn3XFymW4Z+ld+KHWwpCVCxWw53o+k/Q3l9t2
8mITJ+yK5h+D4bU5D8snhpBREFzPh3kYMZ1nTrBPtUne+tGjxxhmetBgT7Iy43hO
d4eQpJ8PEDHUvFfux5HAkyp58fumnjq9/gotjtFKARwpFlQoiLA40QsPInLuSMsk
VLZ76yE7yuPBbHOZOVpHBLImibsg2vrVavT/auhrpoTthuqt3oJkUvidxQ1NHn2N
l5Gf5sECgYEA8fuJMP327heZbkHuWuZ17gKDJvgvg1Vvdco9xcX0p0F9qz9VBT4G
mbK/UqvdL9irfTVWfBkuO5mm8NbtS2JtdLozER6K6NcWM9XHTuvMBoUTP8Yu1LTs
1lyXUkyGFzdDdHse4sS+RKu2V9ykcv4D7byM1WEbu4sYa30IFMRsRxMCgYEAiNpA
PDPePipQDsPd6q3X+8U2pNZpvVwlQNcu8H16Os9k1FD4AkT2rwO+1FsJfCUONyKf
cmwnTrVMOT+DfqPnkUU5IAleKpN6p15Bn2TwRkqc2pAP5Bno6jAQXRQtApDhb6dr
o8hbMWXKwWjFi7m/psyfmki19Ttwf0kRAlufZLECgYEA0KFGD29nTrAaIO3HS6gX
VTXlTdv9WwZ2KuBLkhCyduIIL3VTIEnx9uIeAnEbKv1sHW+3ORecvNYlENC9lpku
Nc2z6bajnUyUX+yZAnpaVeS2S+3awpma+NarwNEpayrChdMBNhLa2OoTNH2Pa3WG
fZpq8i3fczrhuwr5xK31jPECgYBcLIvpQJIm0r77Quc8QnzfodQNMDEmDmM1Cfoe
lpT1+WwTKLnyZVq1RFOKmgqIaDjr39D+82OCOtAtGt9JM8IrcTFpp4WaLZ0GD5Ep
d4XyAh0lhLR/UQg0e8z5tDYgrU55sozoUnFBkyr+G8A6PRCdmuLAusp7ct5xjNf8
+5Me8QKBgQChqG0QQiqnTvKqp8LRng1eUl1n55Pt8WSFr9aZyj3aDgDttcjdBgXQ
5ildvumUidU2JWz5NMC/qoTx1Y9yoUW3snQbLWG3pvRPlRPPRRQwTCCPDMf0RDxI
6Nx/HFJFuW7jR4c8M/rpp8q7Lm693UPRwb/GICbMHKeHP6bMDbTAgw==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,59 @@
-----BEGIN CERTIFICATE-----
MIIFjjCCBHagAwIBAgIQBLkhPdDnB4A4iU84gBdNUjANBgkqhkiG9w0BAQsFADBu
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg
RFYgVExTIENBIC0gRzEwHhcNMTkwODA4MDAwMDAwWhcNMjAwODA3MTIwMDAwWjAd
MRswGQYDVQQDExJtYWxsLnl1c2Vucm9uZy5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCBW+3MpOsI9TBAS7AdULp2x6XPaalRubGDxn5LiDPcV/Zp
46dCxnGg9g+hid6Ix5Lu5EaGUmdd15Ph1uqBnNtwyP56sM3nWIhHvay5SdekNscj
cIlXki3iYairzzdTlFAJUOapOzylPhKg3HHn+RKnpwlhsudXCyZX/xystIoWK0Be
VqoXTWlECfcUoMUxvK2gPyRijLFuZ8L8zaau+d2hZPPBHz+qmWDHuCNIsXY8AAGD
oCggsYfHbke/Dq1EPYFETvJQ3+rojzF7f/Qu4KDBnyITwWUy/EAo8brnVQE6WiSy
7wcp/vVy0mPr/Bx04HfMNgV3mTGPDfpPAMAAbZAjAgMBAAGjggJ3MIICczAfBgNV
HSMEGDAWgBRVdE+yck/1YLpQ0dfmUVyaAYca1zAdBgNVHQ4EFgQUYK0PxpTeIGsq
x3QEPkeBO1otpYIwHQYDVR0RBBYwFIISbWFsbC55dXNlbnJvbmcuY29tMA4GA1Ud
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTAYDVR0g
BEUwQzA3BglghkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
aWNlcnQuY29tL0NQUzAIBgZngQwBAgEwgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEF
BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEoGCCsGAQUFBzAChj5odHRw
Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRW5jcnlwdGlvbkV2ZXJ5d2hlcmVEVlRM
U0NBLUcxLmNydDAJBgNVHRMEAjAAMIIBBQYKKwYBBAHWeQIEAgSB9gSB8wDxAHcA
7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFscYgmYAAABAMASDBG
AiEAs4Ug6AQihwmd35U43LCeg8Jrp683YQM5Z3QHuyl1VY8CIQDlqqLNBDQpA4t9
/KdjCEyaSlE/W17DUoYoEmFQHyQoGgB2AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKES
EoQYdZaBcUVYAAABbHGIJbgAAAQDAEcwRQIhAM74OJiDOG1f6ecIi3n2bcKx25mt
iuUcaynWKNe4xTFoAiBmFncbYFSlvkYJU1rCgbygMqYUeIbQN575ZBnbmH5NLzAN
BgkqhkiG9w0BAQsFAAOCAQEAAzPrPbwGFbBMyhNPtYimOXGqTj0qZ9i9EhQua4oX
JYC5lf0ll8Bhpl8BC8f3FCPEuEtwPp5U5VXSXSbPfHOQfpwRJnmT7LosX3GUzJkU
qoTR3+pu3f//vdfvuli03ayVjI7KaelLeGXZa4RB4dxf+s8m7nqI/Y/1aI77OuoU
pfD15tezAy6zM86Z9fCGXbLCA/acBAvugA8pxvv2HyAoh+yZqZdaseilloa5yzkT
jRBvuwnNm/TOfgw3JnxQtDHM2h0/LTwLx/BS9L7izol9f4hclUG08CAUzU/Lraau
OtY1Ge+qb6p8jQ594chLBXxDgd3/hRLLMHFCxjuo5tyOPw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH
MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc
oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo
lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj
pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h
yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n
wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M
pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf
BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B
SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW
M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV
4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ
sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy
rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg==
-----END CERTIFICATE-----

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 KiB

View File

@@ -0,0 +1,2 @@
"AccessKeyId","AccessKeySecret"
"LTAIuQmZihe6hOk0","2XIDo6yvSDJM2iaEL2EyHBgVF8mMCQ"
1 AccessKeyId AccessKeySecret
2 LTAIuQmZihe6hOk0 2XIDo6yvSDJM2iaEL2EyHBgVF8mMCQ

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

3380
document/sql/mall_data.sql Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
DELETE FROM `dms_seller_bank_account`;
INSERT INTO `dms_seller_bank_account` (`id`, `account_name`, `bank_name`, `bank_card_num`, `open_bank_address`) VALUES
(1, '507科技', '507科技', '507y8-507y8-507y8-507y8', '507科技总部大楼');
DELETE FROM `dms_distribution_dictionary`;
INSERT INTO `dms_distribution_dictionary` (`id`, `name`, `value`, `comment`, `show_status`, `update_time`) VALUES
(1, 'become_consumer', '1914', '成为消费商须购买商品的金额', '1', now()),
(2, 'become_partner', '30000', '成为经销商须购买商品的金额', '1', now()),
(3, 'become_one_diamond_one', '300000', '成为初级合作商须购买商品的金额', '1', now()),
(4, 'become_one_diamond_two', '3', '成为初级合作商须分享经销商的市场数量', '1', now()),
(5, 'become_two_diamond', '3', '成为中级合作商须培养出初级合作商的市场数量', '1', now()),
(6, 'become_three_diamond', '3', '成为高级合作商须培养出中级合作商的市场数量', '1', now()),
(7, 'become_four_diamond', '3', '成为特级合作商须培养出高级合作商的市场数量', '1', now()),
(8, 'consumer_direct_income', '15', '消费商获得分享劳务费的百分比', '1', now()),
(9, 'consumer_indirect_income', '5', '消费商获得服务劳务费的百分比', '1', now()),
(10, 'consumer_buy_again', '8', '成为消费商后再次购买的折扣', '1', now()),
(11, 'partner_direct_income', '20', '经销商获得分享劳务费的百分比', '1', now()),
(12, 'partner_indirect_income', '7', '经销商获得服务劳务费的百分比', '1', now()),
(13, 'partner_buy_again', '6', '成为经销商后再次购买的折扣', '1', now()),
(14, 'partner_store_subsidy', '3', '成为经销商并开店后获得店补的百分比', '1', now()),
(15, 'diamond_direct_income', '28', '合作商获得分享劳务费的百分比', '1', now()),
(16, 'diamond_indirect_income', '9', '合作商获得服务劳务费的百分比', '1', now()),
(17, 'diamond_buy_again', '5', '成为合作商后再次购买的折扣', '1', now()),
(18, 'diamond_store_subsidy', '3', '成为合作商并开店后获得店补的百分比', '1', now()),
(19, 'one_diamond_team_income', '10', '初级合作商获得团队服务劳务费的百分比', '1', now()),
(20, 'two_diamond_team_income', '14', '中级合作商获得团队服务劳务费的百分比', '1', now()),
(21, 'three_diamond_team_income', '16', '高级合作商获得团队服务劳务费的百分比', '1', now()),
(22, 'four_diamond_platform_income', '3', '特级合作商获得平台月度加权平均奖励的百分比', '1', now()),
(23, 'withdraw_poundage', '5', '提现手续费的百分比', '1', now()),
(24, 'withdraw_multiple', '100', '提现金额的整数倍数', '1', now());

View File

@@ -0,0 +1,208 @@
DROP TABLE IF EXISTS `dms_seller_bank_account`;
CREATE TABLE IF NOT EXISTS `dms_seller_bank_account` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`account_name` varchar(100) DEFAULT NULL COMMENT '账户名',
`bank_name` varchar(100) DEFAULT NULL COMMENT '银行名称',
`bank_card_num` varchar(50) DEFAULT NULL COMMENT '银行卡号',
`open_bank_address` varchar(100) DEFAULT NULL COMMENT '开户行地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='商家银行账户表';
DROP TABLE IF EXISTS `dms_offline_payment_record`;
CREATE TABLE IF NOT EXISTS `dms_offline_payment_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`seller_bank_account_id` int(10) DEFAULT NULL COMMENT '商家银行账户id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
`order_sn` varchar(64) DEFAULT NULL COMMENT '订单编号',
`pay_amount` decimal(10,2) DEFAULT NULL COMMENT '支付金额',
`payment_account_name` varchar(100) DEFAULT NULL COMMENT '支付账户名',
`payment_bank_name` varchar(100) DEFAULT NULL COMMENT '支付银行名称',
`payment_bank_card_num` varchar(50) DEFAULT NULL COMMENT '支付银行卡号',
`confirm_status` int(1) DEFAULT NULL COMMENT '确认状态0->未确认1->已确认',
`confirm_time` datetime DEFAULT NULL COMMENT '确认时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='线下支付记录表';
DROP TABLE IF EXISTS `dms_member_bank_account`;
CREATE TABLE IF NOT EXISTS `dms_member_bank_account` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`account_name` varchar(100) DEFAULT NULL COMMENT '账户名',
`bank_name` varchar(100) DEFAULT NULL COMMENT '银行名称',
`bank_card_num` varchar(50) DEFAULT NULL COMMENT '银行卡号',
`open_bank_address` varchar(100) DEFAULT NULL COMMENT '开户行地址',
`default_status` int(1) DEFAULT NULL COMMENT '是否默认0->否1->是',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='会员银行账户表';
DROP TABLE IF EXISTS `dms_member_withdraw_apply`;
CREATE TABLE IF NOT EXISTS `dms_member_withdraw_apply` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`poundage` decimal(10,2) DEFAULT NULL COMMENT '手续费',
`actual_value` decimal(10,2) DEFAULT NULL COMMENT '扣出手续费后的值',
`account_name` varchar(100) DEFAULT NULL COMMENT '账户名',
`bank_name` varchar(100) DEFAULT NULL COMMENT '银行名称',
`bank_card_num` varchar(50) DEFAULT NULL COMMENT '银行卡号',
`open_bank_address` varchar(100) DEFAULT NULL COMMENT '开户行地址',
`payment_status` int(1) DEFAULT NULL COMMENT '付款状态0->未打款1->已打款2->打款失败',
`failed_reason` varchar(255) DEFAULT NULL COMMENT '失败原因',
`payment_time` datetime DEFAULT NULL COMMENT '付款时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='会员提现申请表';
DROP TABLE IF EXISTS `dms_team_level`;
CREATE TABLE IF NOT EXISTS `dms_team_level` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`parent_id` bigint(20) DEFAULT NULL COMMENT '推荐人id',
`level_tree` varchar(1000) DEFAULT NULL COMMENT '层级树',
`level` int(10) DEFAULT NULL COMMENT '当前层级',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='团队层级关系表';
DROP TABLE IF EXISTS `dms_distribution_dictionary`;
CREATE TABLE `dms_distribution_dictionary` (
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`name` VARCHAR(50) NULL DEFAULT NULL COMMENT '名称',
`value` VARCHAR(50) NULL DEFAULT NULL COMMENT '参数值',
`comment` VARCHAR(50) NULL DEFAULT NULL COMMENT '备注',
`show_status` int(1) NULL DEFAULT '1' COMMENT '显示状态( 0: 不显示 1: 显示)',
`update_time` TIMESTAMP NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分销字典表';
DROP TABLE IF EXISTS `dms_member_total_income`;
CREATE TABLE `dms_member_total_income` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`item_type` int(3) NULL DEFAULT NULL COMMENT '项目类型',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员总收入表';
DROP TABLE IF EXISTS `dms_team_total_income`;
CREATE TABLE `dms_team_total_income` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='团队总收入表';
DROP TABLE IF EXISTS `dms_team_total_consume`;
CREATE TABLE `dms_team_total_consume` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='团队累计消费表';
DROP TABLE IF EXISTS `dms_team_member_number`;
CREATE TABLE `dms_team_member_number` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`total` int(10) DEFAULT '0' COMMENT '团队成员总数',
`consumer` int(10) DEFAULT '0' COMMENT '消费者人数',
`partner` int(10) DEFAULT '0' COMMENT '经销商人数',
`one_diamond` int(10) DEFAULT '0' COMMENT '初级合作商人数',
`two_diamond` int(10) DEFAULT '0' COMMENT '中级合作商人数',
`three_diamond` int(10) DEFAULT '0' COMMENT '高级合作商人数',
`four_diamond` int(10) DEFAULT '0' COMMENT '特级合作商人数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='团队成员数量表';
DROP TABLE IF EXISTS `dms_member_trade_record`;
CREATE TABLE `dms_member_trade_record` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`item_type` int(3) NULL DEFAULT NULL COMMENT '项目类型',
`type` int(1) NULL DEFAULT NULL COMMENT '收支类型0->收入1->支出',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`title` VARCHAR(100) NULL DEFAULT NULL COMMENT '交易标题',
`note` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注',
`customer_id` bigint(20) DEFAULT NULL COMMENT '消费者Id',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员账户交易记录表';
DROP TABLE IF EXISTS `dms_member_freeze_account_record`;
CREATE TABLE `dms_member_freeze_account_record` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`item_type` int(3) NULL DEFAULT NULL COMMENT '项目类型',
`type` int(1) NULL DEFAULT NULL COMMENT '收支类型0->收入1->支出',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`title` VARCHAR(100) NULL DEFAULT NULL COMMENT '交易标题',
`note` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注',
`customer_id` bigint(20) DEFAULT NULL COMMENT '消费者Id',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员冻结账户记录表';
DROP TABLE IF EXISTS `dms_platform_income`;
CREATE TABLE `dms_platform_income` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',
`customer_id` bigint(20) DEFAULT NULL COMMENT '消费者Id',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`note` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='平台收入表';
DROP TABLE IF EXISTS `dms_member_transfer_record`;
CREATE TABLE `dms_member_transfer_record` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '转账会员id',
`member_nickname` VARCHAR(255) DEFAULT NULL COMMENT '转账会员昵称',
`receiver_id` bigint(20) DEFAULT NULL COMMENT '接收会员Id',
`receiver_nickname` VARCHAR(255) DEFAULT NULL COMMENT '接收会员昵称',
`receiver_phone` VARCHAR(64) DEFAULT NULL COMMENT '接收会员手机号',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`note` VARCHAR(255) DEFAULT NULL COMMENT '备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员转账记录表';
DROP TABLE IF EXISTS `dms_member_month_reward_record`;
CREATE TABLE `dms_member_month_reward_record` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`member_id` bigint(20) DEFAULT NULL COMMENT '会员id',
`value` decimal(10,2) DEFAULT NULL COMMENT '',
`year` int(4) NULL DEFAULT NULL COMMENT '年份',
`month` int(2) NULL DEFAULT NULL COMMENT '月份',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月度奖励发放记录表';
alter table `ums_member` add column `member_level` int(1) null COMMENT '分销会员等级0->普通会员1->消费商2->经销商3->合作商4->初级合作商5->中级合作商6->高级合作商7->特级合作商))' default '0' AFTER `member_level_id`;
alter table `ums_member` add column `store_status` int(1) null COMMENT '分销实体店状态0->未开店1->已开店)' default '0';
alter table `ums_member` add column `balance` decimal(10,2) null COMMENT '余额' default '0.00';
alter table `ums_member` add column `freeze` decimal(10,2) null COMMENT '冻结余额' default '0.00';
alter table `oms_order` add column settlement_status int(1) null COMMENT '分销结算状态0->未结算1->已结算2->不结算)' default '0';
alter table `oms_order` add column settlement_time datetime null COMMENT '分销结算时间';
alter table `dms_member_withdraw_apply` add column `withdraw_type` int(1) null COMMENT '提现方式(0: 支付宝; 1: 微信; 2: 银行卡)' default null AFTER `open_bank_address`;
alter table `dms_member_withdraw_apply` add column `real_name` varchar(100) null COMMENT '真实姓名' default null AFTER `open_bank_address`;
alter table `dms_member_withdraw_apply` add column `alipay_account` varchar(100) null COMMENT '支付宝账号' default null AFTER `real_name`;
alter table `dms_member_withdraw_apply` add column `wechat_account` varchar(100) null COMMENT '微信账号' default null AFTER `alipay_account`;
alter table `dms_member_withdraw_apply` add column `wechat_nickname` varchar(100) null COMMENT '微信昵称' default null AFTER `wechat_account`;
alter table `ums_member` add column `detail_address` varchar(1000) null COMMENT '详细地址' default null;
alter table `ums_member` add column `real_name` varchar(100) null COMMENT '真实姓名' default null;
alter table `pms_product` add column `distribution_flag` int(1) null COMMENT '分销标志0->不参与1->参与)' default 1;

File diff suppressed because it is too large Load Diff

Binary file not shown.

BIN
keystore.p12 Normal file

Binary file not shown.

124
mall-admin/pom.xml Normal file
View File

@@ -0,0 +1,124 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.buy507.mall</groupId>
<artifactId>mall-admin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mall-admin</name>
<description>mall-admin project for mall</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
<parent>
<groupId>com.buy507.mall</groupId>
<artifactId>mall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.buy507.mall</groupId>
<artifactId>mall-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JWT(Json Web Token)登录支持-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!--redis依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--集成消息队列-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 阿里云OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.5.0</version>
</dependency>
<!--集成logstash-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.8</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- poi excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--<plugin>-->
<!--<groupId>com.spotify</groupId>-->
<!--<artifactId>docker-maven-plugin</artifactId>-->
<!--<version>1.1.0</version>-->
<!--<executions>-->
<!--<execution>-->
<!--<id>build-image</id>-->
<!--<phase>package</phase>-->
<!--<goals>-->
<!--<goal>build</goal>-->
<!--</goals>-->
<!--</execution>-->
<!--</executions>-->
<!--<configuration>-->
<!--<imageName>mall/${project.artifactId}:${project.version}</imageName>-->
<!--<dockerHost>http://39.98.190.128:2375</dockerHost>-->
<!--<baseImage>java:8</baseImage>-->
<!--<entryPoint>["java", "-jar", "-Dspring.profiles.active=prod","/${project.build.finalName}.jar"]</entryPoint>-->
<!--<resources>-->
<!--<resource>-->
<!--<targetPath>/</targetPath>-->
<!--<directory>${project.build.directory}</directory>-->
<!--<include>${project.build.finalName}.jar</include>-->
<!--</resource>-->
<!--</resources>-->
<!--</configuration>-->
<!--</plugin>-->
</plugins>
</build>
</project>

View File

@@ -0,0 +1,25 @@
package com.buy507.mall;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
/**
* 应用启动入口
*/
@EnableTransactionManagement
@SpringBootApplication
public class MallAdminApplication {
public static void main(String[] args) {
SpringApplication.run(MallAdminApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,62 @@
package com.buy507.mall.bo;
import com.buy507.mall.model.UmsAdmin;
import com.buy507.mall.model.UmsPermission;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* SpringSecurity需要的用户详情
*/
public class AdminUserDetails implements UserDetails {
private UmsAdmin umsAdmin;
private List<UmsPermission> permissionList;
public AdminUserDetails(UmsAdmin umsAdmin,List<UmsPermission> permissionList) {
this.umsAdmin = umsAdmin;
this.permissionList = permissionList;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//返回当前用户的权限
return permissionList.stream()
.filter(permission -> permission.getValue()!=null)
.map(permission ->new SimpleGrantedAuthority(permission.getValue()))
.collect(Collectors.toList());
}
@Override
public String getPassword() {
return umsAdmin.getPassword();
}
@Override
public String getUsername() {
return umsAdmin.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return umsAdmin.getStatus().equals(1);
}
}

View File

@@ -0,0 +1,143 @@
package com.buy507.mall.bo;
/**
* Controller层的日志封装类
*/
public class WebLog {
/**
* 操作描述
*/
private String description;
/**
* 操作用户
*/
private String username;
/**
* 操作时间
*/
private Long startTime;
/**
* 消耗时间
*/
private Integer spendTime;
/**
* 根路径
*/
private String basePath;
/**
* URI
*/
private String uri;
/**
* URL
*/
private String url;
/**
* 请求类型
*/
private String method;
/**
* IP地址
*/
private String ip;
private Object parameter;
private Object result;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Long getStartTime() {
return startTime;
}
public void setStartTime(Long startTime) {
this.startTime = startTime;
}
public Integer getSpendTime() {
return spendTime;
}
public void setSpendTime(Integer spendTime) {
this.spendTime = spendTime;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Object getParameter() {
return parameter;
}
public void setParameter(Object parameter) {
this.parameter = parameter;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}

View File

@@ -0,0 +1,29 @@
package com.buy507.mall.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.buy507.mall.service.OmsOrderService;
/**
* 自动确认收货消息的处理者
*/
@Component
@RabbitListener(queues = "mall.receiving.confirm")
public class AutoConfirmReceivingReceiver {
private static Logger LOGGER = LoggerFactory.getLogger(AutoConfirmReceivingReceiver.class);
@Autowired
private OmsOrderService orderService;
@RabbitHandler
public void handle(Long orderId){
LOGGER.info("Confirm receiving receive orderId:{}", orderId);
orderService.confirmReceiving(orderId, true);
}
}

View File

@@ -0,0 +1,37 @@
package com.buy507.mall.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.buy507.mall.dto.ReceivingQueueEnum;
/**
* 自动确认收货消息的发出者
*/
@Component
public class AutoConfirmReceivingSender {
private static Logger LOGGER = LoggerFactory.getLogger(AutoConfirmReceivingSender.class);
@Autowired
private AmqpTemplate amqpTemplate;
public void sendMessage(Long orderId, final long delayTimes){
//给延迟队列发送消息
amqpTemplate.convertAndSend(ReceivingQueueEnum.QUEUE_TTL_RECEIVING_CONFIRM.getExchange(), ReceivingQueueEnum.QUEUE_TTL_RECEIVING_CONFIRM.getRouteKey(), orderId, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//给消息设置延迟毫秒值
message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
return message;
}
});
LOGGER.info("Confirm receiving send orderId:{}", orderId);
}
}

View File

@@ -0,0 +1,42 @@
package com.buy507.mall.component;
import com.buy507.mall.common.api.CommonResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
/**
* HibernateValidator错误结果处理切面
*/
@Aspect
@Component
@Order(2)
public class BindingResultAspect {
@Pointcut("execution(public * com.buy507.mall.controller.*.*(..))")
public void BindingResult() {
}
@Around("BindingResult()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof BindingResult) {
BindingResult result = (BindingResult) arg;
if (result.hasErrors()) {
FieldError fieldError = result.getFieldError();
if(fieldError!=null){
return CommonResult.validateFailed(fieldError.getDefaultMessage());
}else{
return CommonResult.validateFailed();
}
}
}
}
return joinPoint.proceed();
}
}

View File

@@ -0,0 +1,100 @@
package com.buy507.mall.component;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.buy507.mall.mapper.OmsOrderMapper;
import com.buy507.mall.model.OmsOrder;
import com.buy507.mall.service.DistributionService;
/**
* 分销冻结账户队列处理类
* @author wuming
*
*/
@Component
public class DistributeHandlerQueue implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(DistributeHandlerQueue.class);
private static LinkedBlockingQueue<Long> queue = new LinkedBlockingQueue<>();
@Autowired
private DistributionService distributionService;
@Autowired
private ExecutorService executorService;
@Autowired
private OmsOrderMapper orderMapper;
@Override
public void run(String... args) throws Exception {
executorService.execute(new Runnable() {
@Override
public void run() {
while(true) {
Long orderId = null;
try {
LOGGER.info("distribution handler queue size: {}", queue.size());
orderId = queue.take();
LOGGER.info("distribution handler start: {}", orderId);
OmsOrder order = orderMapper.selectByPrimaryKey(orderId);
//1. 分享劳务费和服务劳务费计算
distributionService.computeRefereeCharge(order.getMemberId(), order);
//2. 店补计算
distributionService.computeStoreSubsidy(order.getMemberId(), order);
//3. 上级团队的累计消费信息
distributionService.computeTeamTotalConsume(order.getMemberId(), order);
//4. 团队服务劳务费计算
distributionService.computeTeamIncome(order.getMemberId(), order);
//5. 会员等级计算,根据参数和消费情况计算会员等级
distributionService.computMemberLevel(order.getMemberId(), order);
LOGGER.info("distribution handler end: {}", orderId);
LOGGER.info("distribution handler queue size: {}", queue.size());
} catch (InterruptedException e) {
LOGGER.error("distribution handler failed");
e.printStackTrace();
} catch (Exception e) {
if(orderId != null) {
LOGGER.error("distribution handler failed: {}", orderId);
} else {
LOGGER.error("distribution handler failed");
}
e.printStackTrace();
}
}
}
});
}
/**
* 添加订单到分销处理队列中
* @param order
*/
public static void addOrderToQueue(Long orderId) {
try {
queue.put(orderId);
} catch (InterruptedException e) {
LOGGER.error("distribution add order to queue failed: {}", orderId);
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,23 @@
package com.buy507.mall.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 分销预结算消息的处理者
*/
@Component
@RabbitListener(queues = "mall.distribute.compute")
public class DistributeReceiver {
private static Logger LOGGER = LoggerFactory.getLogger(DistributeReceiver.class);
@RabbitHandler
public void handle(Long orderId){
LOGGER.info("Distribute receive orderId:{}", orderId);
DistributeHandlerQueue.addOrderToQueue(orderId);
}
}

View File

@@ -0,0 +1,24 @@
package com.buy507.mall.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 分销预结算消息的发出者
*/
@Component
public class DistributeSender {
private static Logger LOGGER = LoggerFactory.getLogger(DistributeSender.class);
@Autowired
private AmqpTemplate amqpTemplate;
public void sendMessage(Long orderId){
amqpTemplate.convertAndSend("mall.distribute.compute", orderId);
LOGGER.info("Distribute send orderId:{}", orderId);
}
}

View File

@@ -0,0 +1,61 @@
package com.buy507.mall.component;
import com.buy507.mall.util.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* JWT登录授权过滤器
*/
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
String username = jwtTokenUtil.getUserNameFromToken(authToken);
LOGGER.info("checking username:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
LOGGER.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,65 @@
package com.buy507.mall.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class RedisLock {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisLock.class);
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final int TIMEOUT = 60 * 1000; //超时时间 60s
/**
* 加锁
* @param key 唯一标识
* @param value 当前时间 + 超时时间 也就是时间戳
* @return
*/
public boolean lock(String key, String value){
if(stringRedisTemplate.opsForValue().setIfAbsent(key, value)){//对应setnx命令
//可以成功设置,也就是key不存在
return true;
}
//判断锁超时 - 防止原来的操作异常,没有运行解锁操作 防止死锁
String currentValue = stringRedisTemplate.opsForValue().get(key);
//如果锁过期
if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间
//获取上一个锁的时间value
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);//对应getset如果key存在
//假设两个线程同时进来这里因为key被占用了而且锁过期了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。
//而这里面的getAndSet一次只会一个执行也就是一个执行之后上一个的value已经变成了B。只有一个线程获取的上一个值会是A另一个线程拿到的值是B。
if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){
//oldValue不为空且oldValue等于currentValue也就是校验是不是上个对应的商品时间戳也是防止并发
return true;
}
}
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key, String value){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value) ){
stringRedisTemplate.opsForValue().getOperations().delete(key);//删除key
}
} catch (Exception e) {
LOGGER.error("[Redis distributed lock] failed: {}", e);
}
}
}

View File

@@ -0,0 +1,26 @@
package com.buy507.mall.component;
import cn.hutool.json.JSONUtil;
import com.buy507.mall.common.api.CommonResult;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 当未登录或者token失效访问接口时自定义的返回结果
*/
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
response.getWriter().flush();
}
}

View File

@@ -0,0 +1,28 @@
package com.buy507.mall.component;
import cn.hutool.json.JSONUtil;
import com.buy507.mall.common.api.CommonResult;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 当访问接口没有权限时,自定义的返回结果
*/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
response.getWriter().flush();
}
}

View File

@@ -0,0 +1,123 @@
package com.buy507.mall.component;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.buy507.mall.bo.WebLog;
import io.swagger.annotations.ApiOperation;
import net.logstash.logback.marker.Markers;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 统一日志处理切面
*/
@Aspect
@Component
@Order(1)
public class WebLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
private ThreadLocal<Long> startTime = new ThreadLocal<>();
@Pointcut("execution(public * com.buy507.mall.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
}
@AfterReturning(value = "webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//记录请求信息(通过logstash传入elasticsearch)
WebLog webLog = new WebLog();
Object result = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation log = method.getAnnotation(ApiOperation.class);
webLog.setDescription(log.value());
}
long endTime = System.currentTimeMillis();
String urlStr = request.getRequestURL().toString();
webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
webLog.setIp(request.getRemoteUser());
webLog.setMethod(request.getMethod());
webLog.setParameter(getParameter(method, joinPoint.getArgs()));
webLog.setResult(result);
webLog.setSpendTime((int) (endTime - startTime.get()));
webLog.setStartTime(startTime.get());
webLog.setUri(request.getRequestURI());
webLog.setUrl(request.getRequestURL().toString());
Map<String,Object> logMap = new HashMap<>();
logMap.put("url",webLog.getUrl());
logMap.put("method",webLog.getMethod());
logMap.put("parameter",webLog.getParameter());
logMap.put("spendTime",webLog.getSpendTime());
logMap.put("description",webLog.getDescription());
// LOGGER.info("{}", JSONUtil.parse(webLog));
LOGGER.info(Markers.appendEntries(logMap), JSONUtil.parse(webLog).toString());
return result;
}
/**
* 根据方法和传入的参数获取请求参数
*/
private Object getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.get(0);
} else {
return argList;
}
}
}

View File

@@ -0,0 +1,14 @@
package com.buy507.mall.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* MyBatis配置类
*/
@Configuration
@EnableTransactionManagement
@MapperScan({"com.buy507.mall.mapper","com.buy507.mall.dao"})
public class MyBatisConfig {
}

View File

@@ -0,0 +1,23 @@
package com.buy507.mall.config;
import com.aliyun.oss.OSSClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 阿里云配置类
*/
@Configuration
public class OssConfig {
@Value("${aliyun.oss.endpoint}")
private String ALIYUN_OSS_ENDPOINT;
@Value("${aliyun.oss.accessKeyId}")
private String ALIYUN_OSS_ACCESSKEYID;
@Value("${aliyun.oss.accessKeySecret}")
private String ALIYUN_OSS_ACCESSKEYSECRET;
@Bean
public OSSClient ossClient(){
return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET);
}
}

View File

@@ -0,0 +1,21 @@
package com.buy507.mall.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 分销结算消息队列配置
*/
@Configuration
public class RabbitMqDistributeConfig {
/**
* 分销结算实际消费队列
*/
@Bean
public Queue predistributeQueue() {
return new Queue("mall.distribute.compute");
}
}

View File

@@ -0,0 +1,84 @@
package com.buy507.mall.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.buy507.mall.dto.ReceivingQueueEnum;
/**
* 自动确认收货消息队列配置
*/
@Configuration
public class RabbitMqReceivingConfig {
/**
* 自动确认收货消息实际消费队列所绑定的交换机
*/
@Bean
DirectExchange receivingDirect() {
return (DirectExchange) ExchangeBuilder
.directExchange(ReceivingQueueEnum.QUEUE_RECEIVING_CONFIRM.getExchange())
.durable(true)
.build();
}
/**
* 自动确认收货延迟队列队列所绑定的交换机
*/
@Bean
DirectExchange receivingTtlDirect() {
return (DirectExchange) ExchangeBuilder
.directExchange(ReceivingQueueEnum.QUEUE_TTL_RECEIVING_CONFIRM.getExchange())
.durable(true)
.build();
}
/**
* 自动确认收货实际消费队列
*/
@Bean
public Queue receivingQueue() {
return new Queue(ReceivingQueueEnum.QUEUE_RECEIVING_CONFIRM.getName());
}
/**
* 自动确认收货延迟队列(死信队列)
*/
@Bean
public Queue receivingTtlQueue() {
return QueueBuilder
.durable(ReceivingQueueEnum.QUEUE_TTL_RECEIVING_CONFIRM.getName())
.withArgument("x-dead-letter-exchange", ReceivingQueueEnum.QUEUE_RECEIVING_CONFIRM.getExchange())//到期后转发的交换机
.withArgument("x-dead-letter-routing-key", ReceivingQueueEnum.QUEUE_RECEIVING_CONFIRM.getRouteKey())//到期后转发的路由键
.build();
}
/**
* 将自动确认收货队列绑定到交换机
*/
@Bean
Binding receivingBinding(DirectExchange receivingDirect, Queue receivingQueue){
return BindingBuilder
.bind(receivingQueue)
.to(receivingDirect)
.with(ReceivingQueueEnum.QUEUE_RECEIVING_CONFIRM.getRouteKey());
}
/**
* 将自动确认收货延迟队列绑定到交换机
*/
@Bean
Binding receivingTtlBinding(DirectExchange receivingTtlDirect, Queue receivingTtlQueue){
return BindingBuilder
.bind(receivingTtlQueue)
.to(receivingTtlDirect)
.with(ReceivingQueueEnum.QUEUE_TTL_RECEIVING_CONFIRM.getRouteKey());
}
}

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