谷粒商城项目之分布式基础
目录
- 谷粒商城项目之分布式基础
- 前言
- 1 项目简介
- 2 分布式基础概念
- 3 环境搭建
- 4 springcloud alibaba
- 5 前端
前言
我们在这一阶段正式迈入实战项目—谷粒商城。接下来我们迈入分布式基础之项目环境搭建吧。
1 项目简介
1.1 项目背景
1.1.1 电商模式

1.1.2 谷粒商城
谷粒商城是一个B2C 模式的电商平台,销售自营商品给客户。
1.2 项目架构图
1.2.1 项目微服务架构图

1.2.2 微服务划分图

1.2.3 项目技术&特色

1.2.4 项目前置要求

2 分布式基础概念
2.1 微服务


2.2 集群&分布式&节点


2.3 远程调用

2.4 负载均衡



2.5 服务注册/发现&注册中心

2.6 配置中心

2.7 服务熔断&服务降级
在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。


2.8 API 网关


3 环境搭建
3.1 安装Linux虚拟机
3.1.1 软件下载
-
我们下载 virtual box 这个虚拟机软件 ,官网地址.

傻瓜式安装。

注意设置全局虚拟机路径。因为这个到时候虚拟机默认在c盘当前用户下。当然你c盘很大那无需设置。 -
下载vagrant 这个软件,官网地址,这个软件可以帮助我们自动安装虚拟机和其配置流程。

傻瓜式安装,注意这个软件安装之后需要重启电脑。

vagrant镜像仓库:https://app.vagrantup.com/boxes/search。
这个就是vagrant搭配的第三方centos7镜像。
3.1.2 电脑开启CPU虚拟化


虚拟机软件一般都要开启这个。
3.1.3 安装
- 打开 window cmd 窗口,运行
Vagrant init centos/7,即可初始化一个 centos7 系统 - 运行
vagrant up即可启动虚拟机。系统 root 用户的密码是 vagrant - vagrant 其他常用命令:
-vagrant ssh:自动使用 vagrant 用户连接虚拟机。
-vagrant upload source [destination] [name|id]:上传文件
- https://www.vagrantup.com/docs/cli/init.html Vagrant 命令行

3.1.4 修改ip
默认虚拟机的 ip 地址不是固定 ip,开发不方便.
-
修改 Vagrantfile

windows下cmd命令行中输入ifconfig查看 virtual box的ip网段。

config.vm.network "private_network", ip: "192.168.56.10"将文件这一行注释掉,改为这行代码。

注意:改完之后在命令行输入 vagrant reload 才行,否则会不生效(大坑,否则一直是2个,不会是3个有固定Ip地址的)。 -
重新使用
vagrant up启动机器即可。然后再vagrant ssh连接机器。
3.1.5 使用第三方工具登录

课件上面,修改 PasswordAuthentication yes 这个选项。
网上找到另一种方法来使用第三方ssh登录工具。


成功。
3.1.6 从vagrant用户切换为管理员用户
不知道有没有发现。前面我们登录之后命令符是$,这个是普通用户。但是对于我们开发测试学习来说,普通用户权限太低,对于一些配置文件的修改有很大的限制,所以我们可以使用命令切换到root用户。

3.1.7 修改yum源(下载安装更快)
注意要在root用户下。
- 备份原yum源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup - 使用新yum 源
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo - 生成缓存
yum makecache

3.2 安装docker
sudo —让普通用户可以拥有超级管理员权限。
Docker 安装文档:https://docs.docker.com/install/linux/docker-ce/centos/
3.2.1 卸载系统之前的docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
3.2.2 安装Docker-CE
- 安装必须的依赖
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
- 设置docker repo 的yum 位置
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
- 安装docker,以及docker-cli
sudo yum install docker-ce docker-ce-cli containerd.io
3.3.3 启动docker
sudo systemctl start docker
3.3.4 设置docker 开机自启
sudo systemctl enable docker
**注意:docker 要在root用户下进行测试。
3.3.6 配置docker 镜像加速 — 阿里云镜像加速服务


1. mkdir -p /etc/docker
2. tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://6xvuqzlo.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload #重启docker后台线程sudo systemctl restart docker
3.3 docker 安装MySQL

3.3.1 下载镜像文件
docker pull mysql:5.7
3.3.2 创建实例并启动
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
参数说明
-p 3306:3306:将容器的3306 端口映射到主机的3306 端口
-v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机
-v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机
-v /mydata/mysql/data:/var/lib/mysql/:将配置文件夹挂载到主机
-e MYSQL_ROOT_PASSWORD=root:初始化root 用户的密码
3.3.3 配置MYSQL
主要是配置编码方式,因为默认有些不是utf-8.

所以我们需要进行一些配置,使得变为utf-8,支持中文编码方式,不会乱码。
vi /mydata/mysql/conf/my.cnf
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
注意:解决MySQL 连接慢的问题
在配置文件中加入如下,并重启mysql
[mysqld]
skip-name-resolve
解释:
skip-name-resolve:跳过域名解析
3.3.4 通过容器的mysql 命令行工具连接
docker exec -it mysql mysql -uroot -proot
3.3.5 设置root 远程访问
grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;
3.4 docker 安装redis
3.4.1 下载镜像文件
docker pull redis
3.4.2 创建实例并启动
1. mkdir -p /mydata/redis/conf # 提前创建好redis挂载文件目录,因为不提前设置,mydata/redis/conf/redis.conf 这个redis.conf会被认为是一个文件夹,不会文件
2. touch /mydata/redis/conf/redis.conf # 创建redis 启动的配置文件
3. docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf
在reids.conf中设置开启数据持久化。

redis 自描述文件:
https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf
如果还需要设置其他的可以去上面这个文件中看。
3.4.3 使用redis 镜像执行redis-cli 命令连接
docker exec -it redis redis-cli
3.4.4 Redis 图形化界面

连接自己的redis。

3.5 开发环境统一

因为我们创建了Mysql 和redis的容器,所以设置自启来避免每次都要重新去运行。
3.5.1 Maven
在maven的配置文件中修改镜像源和jdk版本。
配置阿里云镜像
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
配置jdk1.8 编译项目
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
3.5.2 Idea&VsCode
- idea 安装lombok、mybatisx 插件
2.下载 vs code软件----前端必备 https://code.visualstudio.com/
- Vscode 安装开发必备插件
- Vetur —— 语法高亮、智能感知、Emmet 等,包含格式化功能, Alt+Shift+F (格式化全文),Ctrl+K Ctrl+F(格式化选中代码,两个Ctrl需要同时按着)
- EsLint —— 语法纠错
- Auto Close Tag —— 自动闭合HTML/XML 标签
- Auto Rename Tag —— 自动完成另一侧标签的同步修改
- JavaScript(ES6) code snippets — — ES6 语法智能提示以及快速输入, 除js 外还支持.ts,.jsx,.tsx,.html,.vue,省去了配置其支持各种包含js 代码文件的时间
- HTML CSS Support —— 让html 标签上写class 智能提示当前项目所支持的样式
- HTML Snippets —— html 快速自动补全
- Open in browser —— 浏览器快速打开
- Live Server —— 以内嵌服务器方式打开
- Chinese (Simplified) Language Pack for Visual Studio Code —— 中文语言包

3.5.3 安装配置git ---- 配合码云
- 下载git;https://git-scm.com
- 配置git,进入git bash
# 配置用户名
git config --global user.name "username" //(名字)
# 配置邮箱
git config --global user.email "username@email.com" //(注册账号时用的邮箱)
- 配置ssh 免密登录----配合码云
可以参考码云给出的方法。 https://gitee.com/help/articles/4181#article-header0
进入git bash;使用:ssh-keygen -t rsa -C "xxxxx@xxxxx.com"命令。连续三次回车。
一般用户目录下会有

或者cat ~/.ssh/id_rsa.pub
登录进入gitee,在设置里面找到SSH KEY 将.pub 文件的内容粘贴进去
使用ssh -T git@gitee.com测试是否成功即可
Git+码云教程 https://gitee.com/help/articles/4104
3.5.4 创建谷粒商城基础项目仓库----以码云为例

远程仓库创建之后在idea中进行clone下载到本地项目。

3.3.6 创建微服务
老师课件


同理创建其他微服务。
Group:com.atguigu.gulimall
Artifact: gulimall-product gulimall-order gulimall-ware gulimall-member gulimall-coupon
每个微服务必备: web openfeign



项目架构:


在父工程的.gitignore文件中设置我们要忽略的文件。


3.3.7 数据库 准备
创建数据库。

对于数据库设计,这些在后面会讲到。






每一个微服务对应的数据库,进行分库分表。
注意不要设置外键,因为几十万条数据,设置外键,修改,检索需要同时修改。浪费资源。


drop table if exists oms_order;
drop table if exists oms_order_item;
drop table if exists oms_order_operate_history;
drop table if exists oms_order_return_apply;
drop table if exists oms_order_return_reason;
drop table if exists oms_order_setting;
drop table if exists oms_payment_info;
drop table if exists oms_refund_info;
/*==============================================================*/
/* Table: oms_order */
/*==============================================================*/
create table oms_order
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
order_sn char(32) comment '订单号',
coupon_id bigint comment '使用的优惠券',
create_time datetime comment 'create_time',
member_username varchar(200) comment '用户名',
total_amount decimal(18,4) comment '订单总额',
pay_amount decimal(18,4) comment '应付总额',
freight_amount decimal(18,4) comment '运费金额',
promotion_amount decimal(18,4) comment '促销优化金额(促销价、满减、阶梯价)',
integration_amount decimal(18,4) comment '积分抵扣金额',
coupon_amount decimal(18,4) comment '优惠券抵扣金额',
discount_amount decimal(18,4) comment '后台调整订单使用的折扣金额',
pay_type tinyint comment '支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】',
source_type tinyint comment '订单来源[0->PC订单;1->app订单]',
status tinyint comment '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】',
delivery_company varchar(64) comment '物流公司(配送方式)',
delivery_sn varchar(64) comment '物流单号',
auto_confirm_day int comment '自动确认时间(天)',
integration int comment '可以获得的积分',
growth int comment '可以获得的成长值',
bill_type tinyint comment '发票类型[0->不开发票;1->电子发票;2->纸质发票]',
bill_header varchar(255) comment '发票抬头',
bill_content varchar(255) comment '发票内容',
bill_receiver_phone varchar(32) comment '收票人电话',
bill_receiver_email varchar(64) comment '收票人邮箱',
receiver_name varchar(100) comment '收货人姓名',
receiver_phone varchar(32) comment '收货人电话',
receiver_post_code varchar(32) comment '收货人邮编',
receiver_province varchar(32) comment '省份/直辖市',
receiver_city varchar(32) comment '城市',
receiver_region varchar(32) comment '区',
receiver_detail_address varchar(200) comment '详细地址',
note varchar(500) comment '订单备注',
confirm_status tinyint comment '确认收货状态[0->未确认;1->已确认]',
delete_status tinyint comment '删除状态【0->未删除;1->已删除】',
use_integration int comment '下单时使用的积分',
payment_time datetime comment '支付时间',
delivery_time datetime comment '发货时间',
receive_time datetime comment '确认收货时间',
comment_time datetime comment '评价时间',
modify_time datetime comment '修改时间',
primary key (id)
);
alter table oms_order comment '订单';
/*==============================================================*/
/* Table: oms_order_item */
/*==============================================================*/
create table oms_order_item
(
id bigint not null auto_increment comment 'id',
order_id bigint comment 'order_id',
order_sn char(32) comment 'order_sn',
spu_id bigint comment 'spu_id',
spu_name varchar(255) comment 'spu_name',
spu_pic varchar(500) comment 'spu_pic',
spu_brand varchar(200) comment '品牌',
category_id bigint comment '商品分类id',
sku_id bigint comment '商品sku编号',
sku_name varchar(255) comment '商品sku名字',
sku_pic varchar(500) comment '商品sku图片',
sku_price decimal(18,4) comment '商品sku价格',
sku_quantity int comment '商品购买的数量',
sku_attrs_vals varchar(500) comment '商品销售属性组合(JSON)',
promotion_amount decimal(18,4) comment '商品促销分解金额',
coupon_amount decimal(18,4) comment '优惠券优惠分解金额',
integration_amount decimal(18,4) comment '积分优惠分解金额',
real_amount decimal(18,4) comment '该商品经过优惠后的分解金额',
gift_integration int comment '赠送积分',
gift_growth int comment '赠送成长值',
primary key (id)
);
alter table oms_order_item comment '订单项信息';
/*==============================================================*/
/* Table: oms_order_operate_history */
/*==============================================================*/
create table oms_order_operate_history
(
id bigint not null auto_increment comment 'id',
order_id bigint comment '订单id',
operate_man varchar(100) comment '操作人[用户;系统;后台管理员]',
create_time datetime comment '操作时间',
order_status tinyint comment '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】',
note varchar(500) comment '备注',
primary key (id)
);
alter table oms_order_operate_history comment '订单操作历史记录';
/*==============================================================*/
/* Table: oms_order_return_apply */
/*==============================================================*/
create table oms_order_return_apply
(
id bigint not null auto_increment comment 'id',
order_id bigint comment 'order_id',
sku_id bigint comment '退货商品id',
order_sn char(32) comment '订单编号',
create_time datetime comment '申请时间',
member_username varchar(64) comment '会员用户名',
return_amount decimal(18,4) comment '退款金额',
return_name varchar(100) comment '退货人姓名',
return_phone varchar(20) comment '退货人电话',
status tinyint(1) comment '申请状态[0->待处理;1->退货中;2->已完成;3->已拒绝]',
handle_time datetime comment '处理时间',
sku_img varchar(500) comment '商品图片',
sku_name varchar(200) comment '商品名称',
sku_brand varchar(200) comment '商品品牌',
sku_attrs_vals varchar(500) comment '商品销售属性(JSON)',
sku_count int comment '退货数量',
sku_price decimal(18,4) comment '商品单价',
sku_real_price decimal(18,4) comment '商品实际支付单价',
reason varchar(200) comment '原因',
description述 varchar(500) comment '描述',
desc_pics varchar(2000) comment '凭证图片,以逗号隔开',
handle_note varchar(500) comment '处理备注',
handle_man varchar(200) comment '处理人员',
receive_man varchar(100) comment '收货人',
receive_time datetime comment '收货时间',
receive_note varchar(500) comment '收货备注',
receive_phone varchar(20) comment '收货电话',
company_address varchar(500) comment '公司收货地址',
primary key (id)
);
alter table oms_order_return_apply comment '订单退货申请';
/*==============================================================*/
/* Table: oms_order_return_reason */
/*==============================================================*/
create table oms_order_return_reason
(
id bigint not null auto_increment comment 'id',
name varchar(200) comment '退货原因名',
sort int comment '排序',
status tinyint(1) comment '启用状态',
create_time datetime comment 'create_time',
primary key (id)
);
alter table oms_order_return_reason comment '退货原因';
/*==============================================================*/
/* Table: oms_order_setting */
/*==============================================================*/
create table oms_order_setting
(
id bigint not null auto_increment comment 'id',
flash_order_overtime int comment '秒杀订单超时关闭时间(分)',
normal_order_overtime int comment '正常订单超时时间(分)',
confirm_overtime int comment '发货后自动确认收货时间(天)',
finish_overtime int comment '自动完成交易时间,不能申请退货(天)',
comment_overtime int comment '订单完成后自动好评时间(天)',
member_level tinyint(2) comment '会员等级【0-不限会员等级,全部通用;其他-对应的其他会员等级】',
primary key (id)
);
alter table oms_order_setting comment '订单配置信息';
/*==============================================================*/
/* Table: oms_payment_info */
/*==============================================================*/
create table oms_payment_info
(
id bigint not null auto_increment comment 'id',
order_sn char(32) comment '订单号(对外业务号)',
order_id bigint comment '订单id',
alipay_trade_no varchar(50) comment '支付宝交易流水号',
total_amount decimal(18,4) comment '支付总金额',
subject varchar(200) comment '交易内容',
payment_status varchar(20) comment '支付状态',
create_time datetime comment '创建时间',
confirm_time datetime comment '确认时间',
callback_content varchar(4000) comment '回调内容',
callback_time datetime comment '回调时间',
primary key (id)
);
alter table oms_payment_info comment '支付信息表';
/*==============================================================*/
/* Table: oms_refund_info */
/*==============================================================*/
create table oms_refund_info
(
id bigint not null auto_increment comment 'id',
order_return_id bigint comment '退款的订单',
refund decimal(18,4) comment '退款金额',
refund_sn varchar(64) comment '退款交易流水号',
refund_status tinyint(1) comment '退款状态',
refund_channel tinyint comment '退款渠道[1-支付宝,2-微信,3-银联,4-汇款]',
refund_content varchar(5000),
primary key (id)
);
alter table oms_refund_info comment '退款信息';

drop table if exists pms_attr;
drop table if exists pms_attr_attrgroup_relation;
drop table if exists pms_attr_group;
drop table if exists pms_brand;
drop table if exists pms_category;
drop table if exists pms_category_brand_relation;
drop table if exists pms_comment_replay;
drop table if exists pms_product_attr_value;
drop table if exists pms_sku_images;
drop table if exists pms_sku_info;
drop table if exists pms_sku_sale_attr_value;
drop table if exists pms_spu_comment;
drop table if exists pms_spu_images;
drop table if exists pms_spu_info;
drop table if exists pms_spu_info_desc;
/*==============================================================*/
/* Table: pms_attr */
/*==============================================================*/
create table pms_attr
(
attr_id bigint not null auto_increment comment '属性id',
attr_name char(30) comment '属性名',
search_type tinyint comment '是否需要检索[0-不需要,1-需要]',
`value_type` tinyint(4) DEFAULT NULL COMMENT '值类型[0-为单个值,1-可以选择多个值]',
`icon` varchar(255) DEFAULT NULL COMMENT '属性图标',
`value_select` char(255) DEFAULT NULL COMMENT '可选值列表[用逗号分隔]',
`attr_type` tinyint(4) DEFAULT NULL COMMENT '属性类型[0-销售属性,1-基本属性,2-既是销售属性又是基本属性]',
enable bigint comment '启用状态[0 - 禁用,1 - 启用]',
catelog_id bigint comment '所属分类',
show_desc tinyint comment '快速展示【是否展示在介绍上;0-否 1-是】,在sku中仍然可以调整',
primary key (attr_id)
);
alter table pms_attr comment '商品属性';
/*==============================================================*/
/* Table: pms_attr_attrgroup_relation */
/*==============================================================*/
create table pms_attr_attrgroup_relation
(
id bigint not null auto_increment comment 'id',
attr_id bigint comment '属性id',
attr_group_id bigint comment '属性分组id',
attr_sort int comment '属性组内排序',
primary key (id)
);
alter table pms_attr_attrgroup_relation comment '属性&属性分组关联';
/*==============================================================*/
/* Table: pms_attr_group */
/*==============================================================*/
create table pms_attr_group
(
attr_group_id bigint not null auto_increment comment '分组id',
attr_group_name char(20) comment '组名',
sort int comment '排序',
descript varchar(255) comment '描述',
icon varchar(255) comment '组图标',
catelog_id bigint comment '所属分类id',
primary key (attr_group_id)
);
alter table pms_attr_group comment '属性分组';
/*==============================================================*/
/* Table: pms_brand */
/*==============================================================*/
create table pms_brand
(
brand_id bigint not null auto_increment comment '品牌id',
name char(50) comment '品牌名',
logo varchar(2000) comment '品牌logo地址',
descript longtext comment '介绍',
show_status tinyint comment '显示状态[0-不显示;1-显示]',
first_letter char(1) comment '检索首字母',
sort int comment '排序',
primary key (brand_id)
);
alter table pms_brand comment '品牌';
/*==============================================================*/
/* Table: pms_category */
/*==============================================================*/
create table pms_category
(
cat_id bigint not null auto_increment comment '分类id',
name char(50) comment '分类名称',
parent_cid bigint comment '父分类id',
cat_level int comment '层级',
show_status tinyint comment '是否显示[0-不显示,1显示]',
sort int comment '排序',
icon char(255) comment '图标地址',
product_unit char(50) comment '计量单位',
product_count int comment '商品数量',
primary key (cat_id)
);
alter table pms_category comment '商品三级分类';
/*==============================================================*/
/* Table: pms_category_brand_relation */
/*==============================================================*/
create table pms_category_brand_relation
(
id bigint not null auto_increment,
brand_id bigint comment '品牌id',
catelog_id bigint comment '分类id',
brand_name varchar(255),
catelog_name varchar(255),
primary key (id)
);
alter table pms_category_brand_relation comment '品牌分类关联';
/*==============================================================*/
/* Table: pms_comment_replay */
/*==============================================================*/
create table pms_comment_replay
(
id bigint not null auto_increment comment 'id',
comment_id bigint comment '评论id',
reply_id bigint comment '回复id',
primary key (id)
);
alter table pms_comment_replay comment '商品评价回复关系';
/*==============================================================*/
/* Table: pms_product_attr_value */
/*==============================================================*/
create table pms_product_attr_value
(
id bigint not null auto_increment comment 'id',
spu_id bigint comment '商品id',
attr_id bigint comment '属性id',
attr_name varchar(200) comment '属性名',
attr_value varchar(200) comment '属性值',
attr_sort int comment '顺序',
quick_show tinyint comment '快速展示【是否展示在介绍上;0-否 1-是】',
primary key (id)
);
alter table pms_product_attr_value comment 'spu属性值';
/*==============================================================*/
/* Table: pms_sku_images */
/*==============================================================*/
create table pms_sku_images
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
img_url varchar(255) comment '图片地址',
img_sort int comment '排序',
default_img int comment '默认图[0 - 不是默认图,1 - 是默认图]',
primary key (id)
);
alter table pms_sku_images comment 'sku图片';
/*==============================================================*/
/* Table: pms_sku_info */
/*==============================================================*/
create table pms_sku_info
(
sku_id bigint not null auto_increment comment 'skuId',
spu_id bigint comment 'spuId',
sku_name varchar(255) comment 'sku名称',
sku_desc varchar(2000) comment 'sku介绍描述',
catalog_id bigint comment '所属分类id',
brand_id bigint comment '品牌id',
sku_default_img varchar(255) comment '默认图片',
sku_title varchar(255) comment '标题',
sku_subtitle varchar(2000) comment '副标题',
price decimal(18,4) comment '价格',
sale_count bigint comment '销量',
primary key (sku_id)
);
alter table pms_sku_info comment 'sku信息';
/*==============================================================*/
/* Table: pms_sku_sale_attr_value */
/*==============================================================*/
create table pms_sku_sale_attr_value
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
attr_id bigint comment 'attr_id',
attr_name varchar(200) comment '销售属性名',
attr_value varchar(200) comment '销售属性值',
attr_sort int comment '顺序',
primary key (id)
);
alter table pms_sku_sale_attr_value comment 'sku销售属性&值';
/*==============================================================*/
/* Table: pms_spu_comment */
/*==============================================================*/
create table pms_spu_comment
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
spu_id bigint comment 'spu_id',
spu_name varchar(255) comment '商品名字',
member_nick_name varchar(255) comment '会员昵称',
star tinyint(1) comment '星级',
member_ip varchar(64) comment '会员ip',
create_time datetime comment '创建时间',
show_status tinyint(1) comment '显示状态[0-不显示,1-显示]',
spu_attributes varchar(255) comment '购买时属性组合',
likes_count int comment '点赞数',
reply_count int comment '回复数',
resources varchar(1000) comment '评论图片/视频[json数据;[{type:文件类型,url:资源路径}]]',
content text comment '内容',
member_icon varchar(255) comment '用户头像',
comment_type tinyint comment '评论类型[0 - 对商品的直接评论,1 - 对评论的回复]',
primary key (id)
);
alter table pms_spu_comment comment '商品评价';
/*==============================================================*/
/* Table: pms_spu_images */
/*==============================================================*/
create table pms_spu_images
(
id bigint not null auto_increment comment 'id',
spu_id bigint comment 'spu_id',
img_name varchar(200) comment '图片名',
img_url varchar(255) comment '图片地址',
img_sort int comment '顺序',
default_img tinyint comment '是否默认图',
primary key (id)
);
alter table pms_spu_images comment 'spu图片';
/*==============================================================*/
/* Table: pms_spu_info */
/*==============================================================*/
create table pms_spu_info
(
id bigint not null auto_increment comment '商品id',
spu_name varchar(200) comment '商品名称',
spu_description varchar(1000) comment '商品描述',
catalog_id bigint comment '所属分类id',
brand_id bigint comment '品牌id',
weight decimal(18,4),
publish_status tinyint comment '上架状态[0 - 下架,1 - 上架]',
create_time datetime,
update_time datetime,
primary key (id)
);
alter table pms_spu_info comment 'spu信息';
/*==============================================================*/
/* Table: pms_spu_info_desc */
/*==============================================================*/
create table pms_spu_info_desc
(
spu_id bigint not null comment '商品id',
decript longtext comment '商品介绍',
primary key (spu_id)
);
alter table pms_spu_info_desc comment 'spu信息介绍';

drop table if exists sms_coupon;
drop table if exists sms_coupon_history;
drop table if exists sms_coupon_spu_category_relation;
drop table if exists sms_coupon_spu_relation;
drop table if exists sms_home_adv;
drop table if exists sms_home_subject;
drop table if exists sms_home_subject_spu;
drop table if exists sms_member_price;
drop table if exists sms_seckill_promotion;
drop table if exists sms_seckill_session;
drop table if exists sms_seckill_sku_notice;
drop table if exists sms_seckill_sku_relation;
drop table if exists sms_sku_full_reduction;
drop table if exists sms_sku_ladder;
drop table if exists sms_spu_bounds;
/*==============================================================*/
/* Table: sms_coupon */
/*==============================================================*/
create table sms_coupon
(
id bigint not null auto_increment comment 'id',
coupon_type tinyint(1) comment '优惠卷类型[0->全场赠券;1->会员赠券;2->购物赠券;3->注册赠券]',
coupon_img varchar(2000) comment '优惠券图片',
coupon_name varchar(100) comment '优惠卷名字',
num int comment '数量',
amount decimal(18,4) comment '金额',
per_limit int comment '每人限领张数',
min_point decimal(18,4) comment '使用门槛',
start_time datetime comment '开始时间',
end_time datetime comment '结束时间',
use_type tinyint(1) comment '使用类型[0->全场通用;1->指定分类;2->指定商品]',
note varchar(200) comment '备注',
publish_count int(11) comment '发行数量',
use_count int(11) comment '已使用数量',
receive_count int(11) comment '领取数量',
enable_start_time datetime comment '可以领取的开始日期',
enable_end_time datetime comment '可以领取的结束日期',
code varchar(64) comment '优惠码',
member_level tinyint(1) comment '可以领取的会员等级[0->不限等级,其他-对应等级]',
publish tinyint(1) comment '发布状态[0-未发布,1-已发布]',
primary key (id)
);
alter table sms_coupon comment '优惠券信息';
/*==============================================================*/
/* Table: sms_coupon_history */
/*==============================================================*/
create table sms_coupon_history
(
id bigint not null auto_increment comment 'id',
coupon_id bigint comment '优惠券id',
member_id bigint comment '会员id',
member_nick_name varchar(64) comment '会员名字',
get_type tinyint(1) comment '获取方式[0->后台赠送;1->主动领取]',
create_time datetime comment '创建时间',
use_type tinyint(1) comment '使用状态[0->未使用;1->已使用;2->已过期]',
use_time datetime comment '使用时间',
order_id bigint comment '订单id',
order_sn bigint comment '订单号',
primary key (id)
);
alter table sms_coupon_history comment '优惠券领取历史记录';
/*==============================================================*/
/* Table: sms_coupon_spu_category_relation */
/*==============================================================*/
create table sms_coupon_spu_category_relation
(
id bigint not null auto_increment comment 'id',
coupon_id bigint comment '优惠券id',
category_id bigint comment '产品分类id',
category_name varchar(64) comment '产品分类名称',
primary key (id)
);
alter table sms_coupon_spu_category_relation comment '优惠券分类关联';
/*==============================================================*/
/* Table: sms_coupon_spu_relation */
/*==============================================================*/
create table sms_coupon_spu_relation
(
id bigint not null auto_increment comment 'id',
coupon_id bigint comment '优惠券id',
spu_id bigint comment 'spu_id',
spu_name varchar(255) comment 'spu_name',
primary key (id)
);
alter table sms_coupon_spu_relation comment '优惠券与产品关联';
/*==============================================================*/
/* Table: sms_home_adv */
/*==============================================================*/
create table sms_home_adv
(
id bigint not null auto_increment comment 'id',
name varchar(100) comment '名字',
pic varchar(500) comment '图片地址',
start_time datetime comment '开始时间',
end_time datetime comment '结束时间',
status tinyint(1) comment '状态',
click_count int comment '点击数',
url varchar(500) comment '广告详情连接地址',
note varchar(500) comment '备注',
sort int comment '排序',
publisher_id bigint comment '发布者',
auth_id bigint comment '审核者',
primary key (id)
);
alter table sms_home_adv comment '首页轮播广告';
/*==============================================================*/
/* Table: sms_home_subject */
/*==============================================================*/
create table sms_home_subject
(
id bigint not null auto_increment comment 'id',
name varchar(200) comment '专题名字',
title varchar(255) comment '专题标题',
sub_title varchar(255) comment '专题副标题',
status tinyint(1) comment '显示状态',
url varchar(500) comment '详情连接',
sort int comment '排序',
img varchar(500) comment '专题图片地址',
primary key (id)
);
alter table sms_home_subject comment '首页专题表【jd首页下面很多专题,每个专题链接新的页面,展示专题商品信息】';
/*==============================================================*/
/* Table: sms_home_subject_spu */
/*==============================================================*/
create table sms_home_subject_spu
(
id bigint not null auto_increment comment 'id',
name varchar(200) comment '专题名字',
subject_id bigint comment '专题id',
spu_id bigint comment 'spu_id',
sort int comment '排序',
primary key (id)
);
alter table sms_home_subject_spu comment '专题商品';
/*==============================================================*/
/* Table: sms_member_price */
/*==============================================================*/
create table sms_member_price
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
member_level_id bigint comment '会员等级id',
member_level_name varchar(100) comment '会员等级名',
member_price decimal(18,4) comment '会员对应价格',
add_other tinyint(1) comment '可否叠加其他优惠[0-不可叠加优惠,1-可叠加]',
primary key (id)
);
alter table sms_member_price comment '商品会员价格';
/*==============================================================*/
/* Table: sms_seckill_promotion */
/*==============================================================*/
create table sms_seckill_promotion
(
id bigint not null auto_increment comment 'id',
title varchar(255) comment '活动标题',
start_time datetime comment '开始日期',
end_time datetime comment '结束日期',
status tinyint comment '上下线状态',
create_time datetime comment '创建时间',
user_id bigint comment '创建人',
primary key (id)
);
alter table sms_seckill_promotion comment '秒杀活动';
/*==============================================================*/
/* Table: sms_seckill_session */
/*==============================================================*/
create table sms_seckill_session
(
id bigint not null auto_increment comment 'id',
name varchar(200) comment '场次名称',
start_time datetime comment '每日开始时间',
end_time datetime comment '每日结束时间',
status tinyint(1) comment '启用状态',
create_time datetime comment '创建时间',
primary key (id)
);
alter table sms_seckill_session comment '秒杀活动场次';
/*==============================================================*/
/* Table: sms_seckill_sku_notice */
/*==============================================================*/
create table sms_seckill_sku_notice
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
sku_id bigint comment 'sku_id',
session_id bigint comment '活动场次id',
subcribe_time datetime comment '订阅时间',
send_time datetime comment '发送时间',
notice_type tinyint(1) comment '通知方式[0-短信,1-邮件]',
primary key (id)
);
alter table sms_seckill_sku_notice comment '秒杀商品通知订阅';
/*==============================================================*/
/* Table: sms_seckill_sku_relation */
/*==============================================================*/
create table sms_seckill_sku_relation
(
id bigint not null auto_increment comment 'id',
promotion_id bigint comment '活动id',
promotion_session_id bigint comment '活动场次id',
sku_id bigint comment '商品id',
seckill_price decimal comment '秒杀价格',
seckill_count decimal comment '秒杀总量',
seckill_limit decimal comment '每人限购数量',
seckill_sort int comment '排序',
primary key (id)
);
alter table sms_seckill_sku_relation comment '秒杀活动商品关联';
/*==============================================================*/
/* Table: sms_sku_full_reduction */
/*==============================================================*/
create table sms_sku_full_reduction
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'spu_id',
full_price decimal(18,4) comment '满多少',
reduce_price decimal(18,4) comment '减多少',
add_other tinyint(1) comment '是否参与其他优惠',
primary key (id)
);
alter table sms_sku_full_reduction comment '商品满减信息';
/*==============================================================*/
/* Table: sms_sku_ladder */
/*==============================================================*/
create table sms_sku_ladder
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'spu_id',
full_count int comment '满几件',
discount decimal(4,2) comment '打几折',
price decimal(18,4) comment '折后价',
add_other tinyint(1) comment '是否叠加其他优惠[0-不可叠加,1-可叠加]',
primary key (id)
);
alter table sms_sku_ladder comment '商品阶梯价格';
/*==============================================================*/
/* Table: sms_spu_bounds */
/*==============================================================*/
create table sms_spu_bounds
(
id bigint not null auto_increment comment 'id',
spu_id bigint,
grow_bounds decimal(18,4) comment '成长积分',
buy_bounds decimal(18,4) comment '购物积分',
work tinyint(1) comment '优惠生效情况[1111(四个状态位,从右到左);0 - 无优惠,成长积分是否赠送;1 - 无优惠,购物积分是否赠送;2 - 有优惠,成长积分是否赠送;3 - 有优惠,购物积分是否赠送【状态位0:不赠送,1:赠送】]',
primary key (id)
);
alter table sms_spu_bounds comment '商品spu积分设置';

drop table if exists ums_growth_change_history;
drop table if exists ums_integration_change_history;
drop table if exists ums_member;
drop table if exists ums_member_collect_spu;
drop table if exists ums_member_collect_subject;
drop table if exists ums_member_level;
drop table if exists ums_member_login_log;
drop table if exists ums_member_receive_address;
drop table if exists ums_member_statistics_info;
/*==============================================================*/
/* Table: ums_growth_change_history */
/*==============================================================*/
create table ums_growth_change_history
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
create_time datetime comment 'create_time',
change_count int comment '改变的值(正负计数)',
note varchar(0) comment '备注',
source_type tinyint comment '积分来源[0-购物,1-管理员修改]',
primary key (id)
);
alter table ums_growth_change_history comment '成长值变化历史记录';
/*==============================================================*/
/* Table: ums_integration_change_history */
/*==============================================================*/
create table ums_integration_change_history
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
create_time datetime comment 'create_time',
change_count int comment '变化的值',
note varchar(255) comment '备注',
source_tyoe tinyint comment '来源[0->购物;1->管理员修改;2->活动]',
primary key (id)
);
alter table ums_integration_change_history comment '积分变化历史记录';
/*==============================================================*/
/* Table: ums_member */
/*==============================================================*/
create table ums_member
(
id bigint not null auto_increment comment 'id',
level_id bigint comment '会员等级id',
username char(64) comment '用户名',
password varchar(64) comment '密码',
nickname varchar(64) comment '昵称',
mobile varchar(20) comment '手机号码',
email varchar(64) comment '邮箱',
header varchar(500) comment '头像',
gender tinyint comment '性别',
birth date comment '生日',
city varchar(500) comment '所在城市',
job varchar(255) comment '职业',
sign varchar(255) comment '个性签名',
source_type tinyint comment '用户来源',
integration int comment '积分',
growth int comment '成长值',
status tinyint comment '启用状态',
create_time datetime comment '注册时间',
primary key (id)
);
alter table ums_member comment '会员';
/*==============================================================*/
/* Table: ums_member_collect_spu */
/*==============================================================*/
create table ums_member_collect_spu
(
id bigint not null comment 'id',
member_id bigint comment '会员id',
spu_id bigint comment 'spu_id',
spu_name varchar(500) comment 'spu_name',
spu_img varchar(500) comment 'spu_img',
create_time datetime comment 'create_time',
primary key (id)
);
alter table ums_member_collect_spu comment '会员收藏的商品';
/*==============================================================*/
/* Table: ums_member_collect_subject */
/*==============================================================*/
create table ums_member_collect_subject
(
id bigint not null auto_increment comment 'id',
subject_id bigint comment 'subject_id',
subject_name varchar(255) comment 'subject_name',
subject_img varchar(500) comment 'subject_img',
subject_urll varchar(500) comment '活动url',
primary key (id)
);
alter table ums_member_collect_subject comment '会员收藏的专题活动';
/*==============================================================*/
/* Table: ums_member_level */
/*==============================================================*/
create table ums_member_level
(
id bigint not null auto_increment comment 'id',
name varchar(100) comment '等级名称',
growth_point int comment '等级需要的成长值',
default_status tinyint comment '是否为默认等级[0->不是;1->是]',
free_freight_point decimal(18,4) comment '免运费标准',
comment_growth_point int comment '每次评价获取的成长值',
priviledge_free_freight tinyint comment '是否有免邮特权',
priviledge_member_price tinyint comment '是否有会员价格特权',
priviledge_birthday tinyint comment '是否有生日特权',
note varchar(255) comment '备注',
primary key (id)
);
alter table ums_member_level comment '会员等级';
/*==============================================================*/
/* Table: ums_member_login_log */
/*==============================================================*/
create table ums_member_login_log
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
create_time datetime comment '创建时间',
ip varchar(64) comment 'ip',
city varchar(64) comment 'city',
login_type tinyint(1) comment '登录类型[1-web,2-app]',
primary key (id)
);
alter table ums_member_login_log comment '会员登录记录';
/*==============================================================*/
/* Table: ums_member_receive_address */
/*==============================================================*/
create table ums_member_receive_address
(
id bigint not null auto_increment comment 'id',
member_id bigint comment 'member_id',
name varchar(255) comment '收货人姓名',
phone varchar(64) comment '电话',
post_code varchar(64) comment '邮政编码',
province varchar(100) comment '省份/直辖市',
city varchar(100) comment '城市',
region varchar(100) comment '区',
detail_address varchar(255) comment '详细地址(街道)',
areacode varchar(15) comment '省市区代码',
default_status tinyint(1) comment '是否默认',
primary key (id)
);
alter table ums_member_receive_address comment '会员收货地址';
/*==============================================================*/
/* Table: ums_member_statistics_info */
/*==============================================================*/
create table ums_member_statistics_info
(
id bigint not null auto_increment comment 'id',
member_id bigint comment '会员id',
consume_amount decimal(18,4) comment '累计消费金额',
coupon_amount decimal(18,4) comment '累计优惠金额',
order_count int comment '订单数量',
coupon_count int comment '优惠券数量',
comment_count int comment '评价数',
return_order_count int comment '退货数量',
login_count int comment '登录次数',
attend_count int comment '关注数量',
fans_count int comment '粉丝数量',
collect_product_count int comment '收藏的商品数量',
collect_subject_count int comment '收藏的专题活动数量',
collect_comment_count int comment '收藏的评论数量',
invite_friend_count int comment '邀请的朋友数量',
primary key (id)
);
alter table ums_member_statistics_info comment '会员统计信息';

drop table if exists wms_purchase;
drop table if exists wms_purchase_detail;
drop table if exists wms_ware_info;
drop table if exists wms_ware_order_task;
drop table if exists wms_ware_order_task_detail;
drop table if exists wms_ware_sku;
/*==============================================================*/
/* Table: wms_purchase */
/*==============================================================*/
create table wms_purchase
(
id bigint not null auto_increment comment '采购单id',
assignee_id bigint comment '采购人id',
assignee_name varchar(255) comment '采购人名',
phone char(13) comment '联系方式',
priority int(4) comment '优先级',
status int(4) comment '状态',
ware_id bigint comment '仓库id',
amount decimal(18,4) comment '总金额',
create_time datetime comment '创建日期',
update_time datetime comment '更新日期',
primary key (id)
);
alter table wms_purchase comment '采购信息';
/*==============================================================*/
/* Table: wms_purchase_detail */
/*==============================================================*/
create table wms_purchase_detail
(
id bigint not null auto_increment,
purchase_id bigint comment '采购单id',
sku_id bigint comment '采购商品id',
sku_num int comment '采购数量',
sku_price decimal(18,4) comment '采购金额',
ware_id bigint comment '仓库id',
status int comment '状态[0新建,1已分配,2正在采购,3已完成,4采购失败]',
primary key (id)
);
/*==============================================================*/
/* Table: wms_ware_info */
/*==============================================================*/
create table wms_ware_info
(
id bigint not null auto_increment comment 'id',
name varchar(255) comment '仓库名',
address varchar(255) comment '仓库地址',
areacode varchar(20) comment '区域编码',
primary key (id)
);
alter table wms_ware_info comment '仓库信息';
/*==============================================================*/
/* Table: wms_ware_order_task */
/*==============================================================*/
create table wms_ware_order_task
(
id bigint not null auto_increment comment 'id',
order_id bigint comment 'order_id',
order_sn varchar(255) comment 'order_sn',
consignee varchar(100) comment '收货人',
consignee_tel char(15) comment '收货人电话',
delivery_address varchar(500) comment '配送地址',
order_comment varchar(200) comment '订单备注',
payment_way tinyint(1) comment '付款方式【 1:在线付款 2:货到付款】',
task_status tinyint(2) comment '任务状态',
order_body varchar(255) comment '订单描述',
tracking_no char(30) comment '物流单号',
create_time datetime comment 'create_time',
ware_id bigint comment '仓库id',
task_comment varchar(500) comment '工作单备注',
primary key (id)
);
alter table wms_ware_order_task comment '库存工作单';
/*==============================================================*/
/* Table: wms_ware_order_task_detail */
/*==============================================================*/
create table wms_ware_order_task_detail
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
sku_name varchar(255) comment 'sku_name',
sku_num int comment '购买个数',
task_id bigint comment '工作单id',
primary key (id)
);
alter table wms_ware_order_task_detail comment '库存工作单';
/*==============================================================*/
/* Table: wms_ware_sku */
/*==============================================================*/
create table wms_ware_sku
(
id bigint not null auto_increment comment 'id',
sku_id bigint comment 'sku_id',
ware_id bigint comment '仓库id',
stock int comment '库存数',
sku_name varchar(200) comment 'sku_name',
stock_locked int comment '锁定库存',
primary key (id)
);
alter table wms_ware_sku comment '商品库存';
3.3.8 安装Node.js
前端开发,少不了node.js;Node.js 是一个基于Chrome V8 引擎的JavaScript 运行环境。
node 下载 :https://nodejs.org/en/
NPM 是随同NodeJS 一起安装的包管理工具,JavaScript-NPM,Java-Maven;

- 检查版本

- 配置npm 使用淘宝镜像
npm config set registry http://registry.npm.taobao.org/

3.3.9 使用人人开源后台管理系统
去gitee上面搜索人人开源后台管理系统,


- 下载 Git clone到本地

- 将 renren-fast复制到gulimall项目下。并删除掉其中的.git文件夹。


renren-fast-vue到时候用vscode打开,不过一样要删掉里面的git 文件。
- idea中修改renren-fast配置
从模块中不难看出,已经自带了sql文件,所以我们去mysql中创建一个数据库,sql即为该模块下自带的sql.


- application-dev.yml文件中修改成我们自己ip地址,数据库连接。

- 启动renren-fast模块


测试后端,正常。
接下来是前端页面。
- 前端联调。将renren-fast-vue用vs code打开。

控制台输入npm install,如果出现错误,关闭vs code之后重新打开即可。


然后输入npm run dev进行前后端联调。


3.6 使用代码生成器快速CRUD
3.6.1 以gulimall_pms为例,对应的是product微服务。
- 在renren-generator这个模块下修改成我们要连接的数据库名称,账户和密码。

- 在配置文件中修改成我们要使用代码生成器生成的代码。

- 浏览器中访问代码生成器,代码生成器会根据配置文件帮我们生成,然后我们全选下载。下载之后是压缩包,我们可以将压缩包解压。

- 解压之后里面帮我们把代码和sql也一起生成了,main中是代码。


- main->resources->src里面的前端代码我们暂时不需要。


这个里面帮我们也生成了vue代码,目前暂时用不到所以我们需要将其删除。
5. 创建gulimall-common 工程
每一个微服务都需要引入一些相同的依赖和类。我们索性创建一个gulimall-common工程,到时候其他微服务只要引入这个依赖就可以通过maven的传递性引入依赖。

用代码生成器帮我们生成的这些代码,还多依赖还没有,所以我们需要进行一个微服务公共依赖的导入。


3.3.10 gulimall-common依赖引入
-
gulimall-common中,引入mybatis-plus的依赖。

-
我们发现controller中有这个@RequiresPermissions这个注解,我们暂时不需要这个。如果我们只是简单的将其在一个微服务的controller中注解掉,那么其他的微服务怎么办呢?

-
我们直接在代码生成器中controller中的这个注解直接给注释掉。这样以后用这个代码生成器的就不会每个微服务都有了。

修改之后重启代码生成器,然后将其生成的代码生成器的代码中的controller重新复制到gulimall-product模块下。 -
整合mybatis-plus

- 查看mysql

根据版本找对应的mysql的依赖。其实mysql的依赖的很多都可以直接使用最新的。
<!--导入mysql驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
- 引入servlet-api依赖

- 有些暂时用不掉的我们可以删掉。


我们到时候不用这个。

- 创建application.yml中配置数据源


- 注意这里会一个这个会报错。

最开始我的解决办法是:将接口放到utils下面。但是这个解决办法应该是错误的,因为工具类总一般是不会放置接口。

暂时解决办法是:直接导入renren这个依赖

在gulimall-product中简单进行测试。

测试通过。

在数据库中查看是否保存成功。


同时也可以测试其他的

- 同理快速生成其他微服务。比如说gulimall-sms这个优惠服务。





测试:

- ums 用户微服务



- 对每一个微服务设置端口





测试 - 注意这里的product端口改为的是11000,因为深信服vpn占用了10000端口,ware改为12000端口。



- wms 仓储微服务



测试

4 springcloud alibaba
4.1 SpringCloud Alibaba-Nacos[作为注册中心]
4.1.1 下载
地址:https://github.com/alibaba/nacos/releases?page=3 下载这个版本的保持和老师教课一样,避免其他版本问题。

4.1.2 启动nacos:
- 双击bin 中的startup.cmd 文件
- 访问http://localhost:8848/nacos/
- 使用默认的nacos/nacos 进行登录

4.1.3 将微服务注册到nacos中
- 首先,修改pom.xml 文件,引入Nacos Discovery Starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
-
在应用的/src/main/resources/application.properties 配置文件中配置Nacos Server 地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 -
使用@EnableDiscoveryClient 开启服务注册发现功能

-
启动应用,观察nacos 服务列表是否已经注册上服务.
Nacos 使用三步
1、导包nacos-discovery
2、写配置,指定nacos 地址,指定应用的名字
3、开启服务注册发现功能@EnableDiscoveryClient

4.2 注册更多的服务上去,测试使用feign 远程调用

本次以gulimall-coupon远程调用gulimall-member中的方法为例。
4.2.1 首先在gulimall-coupon中的一个controller方法中写一个测试方法,以供被调用。

4.2.2 在gulimall-coupon中写一个feign接口,我们将这个接口都放在feign中。
@FeifnClient注解:告诉springcloud这个接口是一个远程客户端,它要调用远程服务。

4.2.3 在gulimall-coupon的controller方法中调用feign接口。

4.2.4 在调用微服务gulimall-coupon中的启动类上加注解@EnableFeignClients(basePackages = "com.atguigu.gulimall.member.feign" )

4.2.5 启动这两个微服务。
记住:
- 遇到的坑:SpringCloud启动报错
Did you forget to include spring-cloud-starter-loadbalancer
添加匹配当前OpenFeign的负载均衡依赖
由于SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用Ribbon而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错
解决方法 : 加入spring-cloud-loadbalancer依赖, 并且在nacos中排除ribbon依赖,不然loadbalancer无效
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.1.1</version>
</dependency>
gulimall-comon中添加以上注解解决问题。再次进行测试,如下图,测试成功。

Feign 使用三步
1、导包openfeign
2、开启@EnableFeignClients 功能
3、编写接口,进行远程调用
4.3 SpringCloud Alibaba-Nacos[作为配置中心]

4.3.1 pom.xml 引入Nacos Config Starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
4.3.2 在应用的/src/main/resources/bootstrap.properties 配置文件中配置Nacos Config 元数据
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
主要配置应用名和配置中心地址
4.3.3 在nacos 中添加配置
在application.properties中:写这个,先测试。

在nacos 中创建一个应用名.properties 配置文件并编写配置

Nacos Config 数据结构
Nacos Config 主要通过dataId 和group 来唯一确定一条配置。
Nacos Client 从Nacos Server 端获取数据时,调用的是此接口ConfigService.getConfig(String dataId, String group, long timeoutMs)。
4.3.4 在应用中使用@Value 和@RefreshScope
完成上述两步后,应用会从Nacos Config 中获取相应的配置,并添加在Spring Environment的PropertySources 中。这里我们使用@Value 注解来将对应的配置注入到SampleController 的userName 和age 字段,并添加@RefreshScope 打开动态刷新功能。

4.3.5 测试
- 坑,启动报错:



- 踩坑 :配置中心不起作用,真的是这个问题。

4.4 进阶
4.4.1 核心概念


4.4.2 原理

4.4.3 配置文件整合
我们将微服务中的配置文件进行整合。就是在nacos中每一个微服务都有自己的命名空间。每一个微服务的命名空间下有很多个配置列表,这些配置将我们原本在java本地的代码放到注册中心中,然后我们使用boostrap.properties来对命名空间进行设置。具体还可以进行分组。dev prod test这三组。






注意如果在注册中心中找不到就去找默认的,默认的在注册中心中也找不到的时候,就会去找本地的。


4.5 前面遇到的坑的究极问题-------版本问题

突然发现前面为什么和老师一样的代码会报红了,原来是因为springcloud版本不一样导致。怪不得会莫名其妙的报错,为了以后能少点报错,将springboot和springcloud的版本都换成和老师一样的版本吧。

4.5.1 依赖没写错,更改版本仍然报错的解决方法---- 清除缓存


4.5.2 版本切换后测试
- 对于以前踩的坑的解决办法引入的在老师之外的依赖,尝试进行注释掉,测试能否正常运行。
- http://localhost:8000/member/member/coupons
修改之后进行重新测试,并且将前面遇到的坑的解决加入的依赖都给注释掉,发现仍然正常运行,即版本正确。

4.6 springcloud ----- gateway—网关
4.6.1 简介
网关作为流量的入口,常用功能包括路由转发、权限校验、限流控制等。而springcloud gateway作为SpringCloud 官方推出的第二代网关框架,取代了Zuul 网关。

官网文档地址:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.3.RELEASE/single/spring-cloud-gateway.html


4.6.2 核心概念



4.6.2 使用
- 创建gulimall-gateway网关微服务

2. 加入gateway依赖

3. 编写网关配置文件
首先也要将其注册到nacos注册中心,并且为其创建单独的命名空间及端口号。
-
断言(Predicates)

-
过滤器



id:路由的id,没有固定规则但要求唯一
uri:匹配后提供服务的路由地址
path:断言,路径相匹配的进行路由
Query:查询相匹配的进行路由
如果我们在地址栏中访问的地址有qq,或者baidu就会进行转发。
4. 启动测试
测试报错:
-
网关启动报错 坑:
Error creating bean with name 'routeDefinitionRouteLocator' defined in class path resource [org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class]: Unsatisfied dependency expressed through method 'routeDefinitionRouteLocator' parameter 4; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.core.convert.ConversionService' available: expe -
网上搜索到的解决办法,我采用的是排除web内置容器的解决办法。
-
解决办法:

1)解决办法1
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Maven整个生命周期内排除内置容器,排除内置容器导出成war包可以让外部容器运行spring-boot项目-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
2)解决办法2 : 使用 spring-webflux 模块,webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
最后主启动类还要排除数据源:
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class,DataSourceAutoConfiguration.class})
排除的原因是我们将gateway也引入了gulimall-common,而这里面又有数据源配置(mybatis-plus),启动找不到对应的配置就会报错。解决办法就是排除数据源。
最后成功启动项目

5 前端
5.1 VSCode 使用


如果不想手动保存可以选择一下方法:
前端中Live Server 不起作用是因为需要设置自动保存代码或者是自己修改后手动保存才会热更新。
在设置中找到autosave,选择

这个即可,代码会自动保存,热更新插件会成功运行。
5.2 ES6
5.2.1 简介

5.2.2 什么是ECMAScript


5.2.3 ES6 新特性
快捷键:
shift + ! — 快速生成html模板
代码格式化:shift + alt +f
1、 let 声明变量
// var 声明的变量往往会越域
// let 声明的变量有严格局部作用域
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
// var 可以声明多次
// let 只能声明一次
var m = 1
var m = 2
let n = 3
// let n = 4
console.log(m) // 2
console.log(n) // Identifier 'n' has already been declared
// var 会变量提升
// let 不存在变量提升
console.log(x); // undefined
var x = 10;
console.log(y); //ReferenceError: y is not defined
let y = 20;
2、 const 声明常量(只读变量)
// 1. 声明之后不允许改变
// 2. 一但声明必须初始化,否则会报错
const a = 1;
a = 3; //Uncaught TypeError: Assignment to constant variable.
3、解构表达式
1)、数组解构
let arr = [1,2,3];
//以前我们想获取其中的值,只能通过角标。ES6 可以这样:
const [x,y,z] = arr;// x,y,z 将与arr 中的每个位置对应来取值
// 然后打印
console.log(x,y,z);
2)、对象解构
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
// 解构表达式获取值,将person 里面每一个属性和左边对应赋值
const { name, age, language } = person;
// 等价于下面
// const name = person.name;
// const age = person.age;
// const language = person.language;
// 可以分别打印
console.log(name);
console.log(age);
console.log(language);
//扩展:如果想要将name 的值赋值给其他变量,可以如下,nn 是新的变量名
const { name: nn, age, language } = person;
console.log(nn);
console.log(age);
console.log(language);
4、字符串扩展
1)、几个新的API
ES6 为字符串扩展了几个新的API:
includes():返回布尔值,表示是否找到了参数字符串。startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let str = "hello.vue";
console.log(str.startsWith("hello"));//true
console.log(str.endsWith(".vue"));//true
console.log(str.includes("e"));//true
console.log(str.includes("hello"));//true
2)、字符串模板
模板字符串相当于加强版的字符串,用反引号`,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
// 1、多行字符串
let ss = `
<div>
<span>hello world<span>
</div>
`
console.log(ss)
// 2、字符串插入变量和表达式。变量名写在${} 中,${} 中可以放
入JavaScript 表达式。
let name = "张三";
let age = 18;
let info = `我是${name},今年${age}了`;
console.log(info)
// 3、字符串中调用函数
function fun() {
return "这是一个函数"
}
let sss = `O(∩_∩)O 哈哈~,${fun()}`;
console.log(sss); // O(∩_∩)O 哈哈~,这是一个函数
5、函数优化
1)、函数参数默认值
//在ES6 以前,我们无法给一个函数参数设置默认值,只能采用变通写法:
function add(a, b) {
// 判断b 是否为空,为空就给默认值1
b = b || 1;
return a + b;
}
// 传一个参数
console.log(add(10));
//现在可以这么写:直接给参数写上默认值,没传就会自动使用默认值
function add2(a , b = 1) {
return a + b;
}
// 传一个参数
console.log(add2(10));
2)、不定参数
不定参数用来表示不确定参数个数,形如,…变量名,由…加上一个具名参数标识符组成。具名参数只能放在参数列表的最后,并且有且只有一个不定参数。
function fun(...values) {
console.log(values.length)
}
fun(1, 2) //2
fun(1, 2, 3, 4) //4
3)、箭头函数
ES6 中定义函数的简写方式:
- 一个参数时:
//以前声明一个方法
// var print = function (obj) {
// console.log(obj);
// }
// 可以简写为:
var print = obj => console.log(obj);
// 测试调用
print(100);
- 多个参数:
// 两个参数的情况:
var sum = function (a, b) {
return a + b;
}
// 简写为:
//当只有一行语句,并且需要返回结果时,可以省略{} , 结果会自动返回。
var sum2 = (a, b) => a + b;
//测试调用
console.log(sum2(10, 10));//20
// 代码不止一行,可以用`{}`括起来
var sum3 = (a, b) => {
c = a + b;
return c;
};
//测试调用
console.log(sum3(10, 20));//30
4)、实战:箭头函数结合解构表达式
//需求,声明一个对象,hello 方法需要对象的个别属性
//以前的方式:
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
function hello(person) {
console.log("hello," + person.name)
}
//现在的方式
var hello2 = ({ name }) => { console.log("hello," + name) };
//测试
hello2(person);
6、对象优化
1)、新增的API
ES6 给Object 拓展了许多新的方法,如:
- keys(obj):获取对象的所有key 形成的数组
- values(obj):获取对象的所有value 形成的数组
- entries(obj):获取对象的所有key 和value 形成的二维数组。格式:
[[k1,v1],[k2,v2],...] - assign(dest, …src) :将多个src 对象的值拷贝到dest 中。(第一层为深拷贝,第二层为浅
拷贝)
const person = {
name: "jack",
age: 21,
language: ['java', 'js', 'css']
}
console.log(Object.keys(person));//["name", "age", "language"]
console.log(Object.values(person));//["jack", 21, Array(3)]
console.log(Object.entries(person));//[Array(2), Array(2), Array(2)]
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
//Object.assign 方法的第一个参数是目标对象,后面的参数都是源对象。
Object.assign(target, source1, source2);
console.log(target)//{a: 1, b: 2, c: 3}
2)、声明对象简写
const age = 23
const name = "张三"
// 传统
const person1 = { age: age, name: name }
console.log(person1)
// ES6:属性名和属性值变量名一样,可以省略
const person2 = { age, name }
console.log(person2) //{age: 23, name: "张三"}
3)、对象的函数属性简写
let person = {
name: "jack",
// 以前:
eat: function (food) {
console.log(this.name + "在吃" + food);
},
// 箭头函数版:这里拿不到this
eat2: food => console.log(person.name + "在吃" + food),
// 简写版:
eat3(food) {
console.log(this.name + "在吃" + food);
}
}
person.eat("apple");
4)、对象拓展运算符
拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
// 1、拷贝对象(深拷贝)
let person1 = { name: "Amy", age: 15 }
let someone = { ...person1 }
console.log(someone) //{name: "Amy", age: 15}
// 2、合并对象
let age = { age: 15 }
let name = { name: "Amy" }
let person2 = { ...age, ...name } //如果两个对象的字段名重复,后面对象字
段值会覆盖前面对象的字段值
console.log(person2) //{age: 15, name: "Amy"}
7、map 和reduce
数组中新增了map 和reduce 方法。
1)、map
map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
let arr = ['1', '20', '-5', '3'];
console.log(arr)
arr = arr.map(s => parseInt(s));
console.log(arr)
2)、reduce
语法:
arr.reduce(callback,[initialValue])
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元
素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调
用reduce 的数组。
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用reduce 的数组)
initialValue (作为第一次调用callback 的第一个参数。)
示例:
const arr = [1,20,-5,3];
//没有初始值:
console.log(arr.reduce((a,b)=>a+b));//19
console.log(arr.reduce((a,b)=>a*b));//-300
//指定初始值:
console.log(arr.reduce((a,b)=>a+b,1));//20
console.log(arr.reduce((a,b)=>a*b,0));//-0
8、Promise
在JavaScript 的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript 的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现。一旦有一连串的ajax 请求a,b,c,d… 后面的请求依赖前面的请求结果,就需要层层嵌套。这种缩进和层
层嵌套的方式,非常容易造成上下文代码混乱,我们不得不非常小心翼翼处理内层函数与外层函数的数据,一旦内层函数使用了上层函数的变量,这种混乱程度就会加剧…总之,这种层叠上下文的层层嵌套方式,着实增加了神经的紧张程度。
案例:用户登录,并展示该用户的各科成绩。在页面发送两次请求:
- 查询用户,查询成功说明可以登录
- 查询用户成功,查询科目
- 根据科目的查询结果,获取去成绩
分析:此时后台应该提供三个接口,一个提供用户查询接口,一个提供科目的接口,一个提供各科成绩的接口,为了渲染方便,最好响应json 数据。在这里就不编写后台接口了,而是提供三个json 文件,直接提供json 数据,模拟后台接口:
user.json:
{
"id": 1,
"name": "zhangsan",
"password": "123456"
}
user_corse_1.json:
{
"id": 10,
"name": "chinese"
}
corse_score_10.json:
{
"id": 100,
"score": 90
}
//回调函数嵌套的噩梦:层层嵌套。
$.ajax({
url: "mock/user.json",
success(data) {
console.log("查询用户:", data);
$.ajax({
url: `mock/user_corse_${data.id}.json`,
success(data) {
console.log("查询到课程:", data);
$.ajax({
url: `mock/corse_score_${data.id}.json`,
success(data) {
console.log("查询到分数:", data);
},
error(error) {
console.log("出现异常了:" + error);
}
});
},
error(error) {
console.log("出现异常了:" + error);
}
});
},
error(error) {
console.log("出现异常了:" + error);
}
});
我们可以通过Promise 解决以上问题。
1)、Promise 语法
const promise = new Promise(function (resolve, reject) {
// 执行异步操作
if (/* 异步操作成功*/) {
resolve(value);// 调用resolve,代表Promise 将返回成功的结果
} else {
reject(error);// 调用reject,代表Promise 会返回失败结果
}
});
使用箭头函数可以简写为:
const promise = new Promise((resolve, reject) =>{
// 执行异步操作
if (/* 异步操作成功*/) {
resolve(value);// 调用resolve,代表Promise 将返回成功的结果
} else {
reject(error);// 调用reject,代表Promise 会返回失败结果
}
});
这样,在promise 中就封装了一段异步执行的结果。
2)、处理异步结果
如果我们想要等待异步执行完成,做一些事情,我们可以通过promise 的then 方法来实现。
如果想要处理promise 异步执行失败的事件,还可以跟上catch:
promise.then(function (value) {
// 异步执行成功后的回调
}).catch(function (error) {
// 异步执行失败后的回调
})
3)、Promise 改造以前嵌套方式
new Promise((resolve, reject) => {
$.ajax({
url: "mock/user.json",
success(data) {
console.log("查询用户:", data);
resolve(data.id);
},
error(error) {
console.log("出现异常了:" + error);
}
});
}).then((userId) => {
return new Promise((resolve, reject) => {
$.ajax({
url: `mock/user_corse_${userId}.json`,
success(data) {
console.log("查询到课程:", data);
resolve(data.id);
},
error(error) {
console.log("出现异常了:" + error);
}
});
});
}).then((corseId) => {
console.log(corseId);
$.ajax({
url: `mock/corse_score_${corseId}.json`,
success(data) {
console.log("查询到分数:", data);
},
error(error) {
console.log("出现异常了:" + error);
}
});
});
4)、优化处理
优化:通常在企业开发中,会把promise 封装成通用方法,如下:封装了一个通用的get 请求方法
let get = function (url, data) { // 实际开发中会单独放到common.js 中
return new Promise((resolve, reject) => {
$.ajax({
url: url,
type: "GET",
data: data,
success(result) {
resolve(result);
},
error(error) {
reject(error);
}
});
})
}
// 使用封装的get 方法,实现查询分数
get("mock/user.json").then((result) => {
console.log("查询用户:", result);
return get(`mock/user_corse_${result.id}.json`);
}).then((result) => {
console.log("查询到课程:", result);
return get(`mock/corse_score_${result.id}.json`)
}).then((result) => {
console.log("查询到分数:", result);
}).catch(() => {
console.log("出现异常了:" + error);
});
通过比较,我们知道了Promise 的扁平化设计理念,也领略了这种上层设计带来的好处。
我们的项目中会使用到这种异步处理的方式;
9、模块化
1)、什么是模块化
模块化就是把代码进行拆分,方便重复利用。类似java 中的导包:要使用一个包,必须先导包。而JS 中没有包的概念,换来的是模块。
模块功能主要由两个命令构成:export和import。
export命令用于规定模块的对外接口。import命令用于导入其他模块提供的功能。
2)、export
比如我定义一个js 文件:hello.js,里面有一个对象:
const util = {
sum(a,b){
return a + b;
}
}
我可以使用export 将这个对象导出:
const util = {
sum(a,b){
return a + b;
}
}
export {util};
当然,也可以简写为:
export const util = {
sum(a,b){
return a + b;
}
}
export不仅可以导出对象,一切JS 变量都可以导出。比如:基本类型变量、函数、数组、对象。
当要导出多个值时,还可以简写。比如我有一个文件:user.js:
var name = "jack"
var age = 21
export {name,age}
省略名称
上面的导出代码中,都明确指定了导出的变量名,这样其它人在导入使用时就必须准确写出
变量名,否则就会出错。
因此js 提供了default关键字,可以对导出的变量名进行省略
例如:
// 无需声明对象的名字
export default {
sum(a,b){
return a + b;
}
}
这样,当使用者导入时,可以任意起名字。
3)、import
使用export命令定义了模块的对外接口以后,其他JS 文件就可以通过import命令加载这个模块。
例如我要使用上面导出的util:
// 导入util
import util from 'hello.js'
// 调用util 中的属性
util.sum(1,2)
要批量导入前面导出的name 和age:
import {name, age} from 'user.js'
console.log(name + " , 今年"+ age +"岁了")
但是上面的代码暂时无法测试,因为浏览器目前还不支持ES6 的导入和导出功能。除非借助于工具,把ES6 的语法进行编译降级到ES5,比如Babel-cli工具我们暂时不做测试,大家了解即可。
5.3 Node.js
前端开发,少不了node.js;Node.js 是一个基于Chrome V8 引擎的JavaScript 运行环境。
http://nodejs.cn/api/
我们关注与node.js 的npm 功能就行;
NPM 是随同NodeJS 一起安装的包管理工具,JavaScript-NPM,Java-Maven;
1)、官网下载安装node.js,并使用node -v 检查版本
2)、配置npm 使用淘宝镜像
npm config set registry http://registry.npm.taobao.org/
3)、大家如果npm install 安装依赖出现chromedriver 之类问题,先在项目里运行下面命令
npm install chromedriver --chromedriver_cdnurl=http://cdn.npm.taobao.org/dist/chromedriver
然后再运行npm install
5.4 Vue
1、MVVM 思想
- M:即Model,模型,包括数据和一些基本操作
- V:即View,视图,页面渲染结果
- VM:即View-Model,模型与视图间的双向操作(无需开发人员干涉)
在MVVM 之前,开发人员从后端获取需要的数据模型,然后要通过DOM 操作Model 渲染到View 中。而后当用户操作视图,我们还需要通过DOM 获取View 中的数据,然后同步到Model 中。
而MVVM 中的VM 要做的事情就是把DOM 操作完全封装起来,开发人员不用再关心Model和View 之间是如何互相影响的:
- 只要我们Model 发生了改变,View 上自然就会表现出来。
- 当用户修改了View,Model 中的数据也会跟着改变。
把开发人员从繁琐的DOM 操作中解放出来,把关注点放在如何操作Model 上。

2、Vue 简介
Vue (读音/vjuː/,类似于view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
官网:https://cn.vuejs.org/
参考:https://cn.vuejs.org/v2/guide/
Git 地址:https://github.com/vuejs
3、入门案例
1)、安装
官网文档提供了3 种安装方式:
- 直接script 引入本地vue 文件。需要通过官网下载vue 文件。
- 通过script 引入CDN 代理。需要联网,生产环境可以使用这种方式
- 通过npm 安装。这种方式也是官网推荐的方式,需要nodejs 环境。
本课程就采用第三种方式


这里没有vue.js文件的,vue版本错了,npm install vue@2 重装一下,npm默认装的是vue3.0
2)、创建示例项目
1、新建文件夹hello-vue,并使用vscode 打开
2、使用vscode 控制台,npm install -y;
项目会生成package-lock.json 文件,类似于maven 项目的pom.xml 文件。
3、使用npm install vue,给项目安装vue;项目下会多node_modules 目录,并且在下面有
一个vue 目录。

3)、HelloWorld
在hello.html 中,我们编写一段简单的代码。
h2 中要输出一句话:xx 非常帅。前面的xx是要渲染的数据。

4)、vue 声明式渲染
页面代码
<div id="app">
<h1>{{name}},非常帅!!!</h1>
</div>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
name: "张三"
}
});
</script>
</body>

5)、双向绑定
我们对刚才的案例进行简单修改:
<div id="app">
<input type="text" v-model="num">
<h2>
{{name}},非常帅!!!有{{num}}个人为他点赞。
</h2>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 创建vue 实例
let app = new Vue({
el: "#app", // el 即element,该vue 实例要渲染的页面元素
data: { // 渲染页面需要的数据
name: "张三",
num: 5
}
});
</script>

6)、事件处理
给页面添加一个按钮:
<body>
<div id="app">
<input type="text" v-model="num">
<button v-on:click="num++">关注</button>
<h2>
{{name}},非常帅!!!有{{num}}个人为他点赞。
</h2>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 创建vue 实例
let app = new Vue({
el: "#app", // el 即element,该vue 实例要渲染的页面元素
data: { // 渲染页面需要的数据
name: "张三",
num: 5
}
});
</script>
</body>


简单使用总结:
1)、使用Vue 实例管理DOM
2)、DOM 与数据/事件等进行相关绑定
3)、我们只需要关注数据,事件等处理,无需关心视图如何进行修改
4、概念
1、创建Vue 实例
每个Vue 应用都是通过用Vue 函数创建一个新的Vue 实例开始的:
let app = new Vue({
});
在构造函数中传入一个对象,并且在对象中声明各种Vue 需要的数据和方法,包括:
- el
- data
- methods
2、模板或元素
每个Vue 实例都需要关联一段Html 模板,Vue 会基于此模板进行视图渲染。
我们可以通过el 属性来指定。
例如一段html 模板:
<div id="app">
</div>
然后创建Vue 实例,关联这个div
let vm = new Vue({
el: "#app"
})
这样,Vue 就可以基于id 为app的div 元素作为模板进行渲染了。在这个div 范围以外的部
分是无法使用vue 特性的。
3、数据
当Vue 实例被创建时,它会尝试获取在data 中定义的所有属性,用于视图的渲染,并且监视data 中的属性变化,当data 发生改变,所有相关的视图都将重新渲染,这就是“响应式“系统。
html:
<div id="app">
<input type="text" v-model="name" />
</div>
JS:
let vm = new Vue({
el: "#app",
data: {
name: "刘德华"
}
})
4、方法
Vue 实例中除了可以定义data 属性,也可以定义方法,并且在Vue 实例的作用范围内使用。
Html:
<div id="app">
{{num}}
<button v-on:click="add">加</button>
</div>
JS:
let vm = new Vue({
el: "#app",
data: {
num: 0
},
methods: {
add: function () {
// this 代表的当前vue 实例
this.num++;
}
}
})
5、安装vue-devtools 方便调试



6、安装vscode 的vue 插件

安装这个插件就可以有语法提示
5、指令
什么是指令?
- 指令(Directives) 是带有
v-前缀的特殊特性。 - 指令特性的预期值是:单个JavaScript 表达式。
- 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM
例如我们在入门案例中的v-on,代表绑定事件。
1、插值表达式
1)、花括号
格式:{{表达式}}
说明:
- 该表达式支持JS 语法,可以调用js 内置函数(必须有返回值)
- 表达式必须有返回结果。例如1 + 1,没有结果的表达式不允许使用,如:let a = 1 + 1;
- 可以直接获取Vue 实例中定义的数据或函数
2)、插值闪烁
使用{{}}方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的{{}},加载完毕后才显示正确数据,我们称为插值闪烁。
我们将网速调慢一些,然后刷新页面,试试看刚才的案例:

3)、v-text 和v-html
可以使用v-text 和v-html 指令来替代{{}}
说明:
- v-text:将数据输出到元素内部,如果输出的数据有HTML 代码,会作为普通文本输出
- v-html:将数据输出到元素内部,如果输出的数据有HTML 代码,会被渲染
示例:
<div id="app">
v-text:<span v-text="hello"></span> <br />
v-html:<span v-html="hello"></span>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
hello: "<h1>大家好</h1>"
}
})
</script>
效果:

并且不会出现插值闪烁,当没有数据时,会显示空白或者默认数据。
2、v-bind
html 属性不能使用双大括号形式绑定,我们使用v-bind 指令给HTML 标签属性绑定值;
而且在将v-bind 用于class 和style 时,Vue.js 做了专门的增强。
1)、绑定class
<div class="static" v-bind:class="{ active: isActive, 'text-danger'
: hasError }">
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
isActive: true,
hasError: false
}
})
</script>
2)、绑定style
v-bind:style 的对象语法十分直观,看着非常像CSS,但其实是一个JavaScript 对象。style属性名可以用驼峰式(camelCase) 或短横线分隔(kebab-case,这种方式记得用单引号括起来) 来命名。
例如:font-size–>fontSize
<div id="app" v-bind:style="{ color: activeColor, fontSize: fontSiz
e + 'px' }"></div>
<script>
let vm = new Vue({
el: "#app",
data: {
activeColor: 'red',
fontSize: 30
}
})
</script>
结果:
3)、绑定其他任意属性
<div id="app" v-bind:style="{ color: activeColor, fontSize: fontS
ize + 'px' }" v-bind:user="userName">
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
activeColor: 'red',
fontSize: 30,
userName: 'zhangsan'
}
})
</script>
4)、v-bind 缩写
<div id="app" :style="{ color: activeColor, fontSize: fontSize +
'px' }" :user="userName">
</div>
3、v-model
刚才的v-text、v-html、v-bind 可以看做是单向绑定,数据影响了视图渲染,但是反过来就不
行。接下来学习的v-model 是双向绑定,视图(View)和模型(Model)之间会互相影响。
既然是双向绑定,一定是在视图中可以修改数据,这样就限定了视图的元素类型。目前
v-model 的可使用元素有:
- input
- select
- textarea
- checkbox
- radio
- components(Vue 中的自定义组件)
基本上除了最后一项,其它都是表单的输入项。
示例:
<div id="app">
<input type="checkbox" v-model="language" value="Java" />Java<br />
<input type="checkbox" v-model="language" value="PHP" />PHP<br />
<input type="checkbox" v-model="language" value="Swift" />Swift<br />
<h1>
你选择了:{{language.join(',')}}
</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
language: []
}
})
</script>
- 多个
CheckBox对应一个model 时,model 的类型是一个数组,单个checkbox 值默认是boolean 类型 - radio 对应的值是input 的value 值
text和textarea默认对应的model 是字符串select单选对应字符串,多选对应也是数组

4、v-on
1、基本用法
v-on 指令用于给页面元素绑定事件。
语法: v-on:事件名=“js 片段或函数名”
示例:
<div id="app">
<!--事件中直接写js 片段-->
<button v-on:click="num++">点赞</button>
<!--事件指定一个回调函数,必须是Vue 实例中定义的函数-->
<button v-on:click="decrement">取消</button>
<h1>有{{num}}个赞</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
decrement() {
this.num--; //要使用data 中的属性,必须this.属性名
}
}
})
</script>
另外,事件绑定可以简写,例如v-on:click='add'可以简写为@click='add'
2、事件修饰符
在事件处理程序中调用event.preventDefault() 或event.stopPropagation() 是非常常见的
需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,
而不是去处理DOM 事件细节。
为了解决这个问题,Vue.js 为v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来
表示的。
.stop:阻止事件冒泡到父元素.prevent:阻止默认事件发生.capture:使用事件捕获模式.self:只有元素自身触发事件才执行。(冒泡或捕获的都不执行).once:只执行一次
<div id="app">
<!--右击事件,并阻止默认事件发生-->
<button v-on:contextmenu.prevent="num++">点赞</button>
<br />
<!--右击事件,不阻止默认事件发生-->
<button v-on:contextmenu="decrement($event)">取消</button>
<br />
<h1>有{{num}}个赞</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
decrement(ev) {
// ev.preventDefault();
this.num--;
}
}
})
</script>
效果:右键“点赞”,不会触发默认的浏览器右击事件;右键“取消”,会触发默认的浏览器右击事件)
3. 按键修饰符

4、组合按钮


5、v-for
遍历数据渲染页面是非常常用的需求,Vue 中通过v-for 指令来实现。
1、遍历数组
语法:v-for=“item in items”
- items:要遍历的数组,需要在vue 的data 中定义好。
- item:迭代得到的当前正在遍历的元素
示例:
<div id="app">
<ul>
<li v-for="user in users">
{{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
users: [
{ name: '柳岩', gender: '女', age: 21 },
{ name: '张三', gender: '男', age: 18 },
{ name: '范冰冰', gender: '女', age: 24 },
{ name: '刘亦菲', gender: '女', age: 18 },
{ name: '古力娜扎', gender: '女', age: 25 }
]
},
})
</script>
效果:

2、数组角标

3、遍历对象



4、Key


6、v-if 和v-show




7、v-else 和v-else-if
v-else 元素必须紧跟在带v-if 或者v-else-if 的元素的后面,否则它将不会被识别。
示例:
<div id="app">
<button v-on:click="random=Math.random()">点我呀
</button><span>{{random}}</span>
<h1 v-if="random >= 0.75">
看到我啦?!v-if >= 0.75
</h1>
<h1 v-else-if="random > 0.5">
看到我啦?!v-else-if > 0.5
</h1>
<h1 v-else-if="random > 0.25">
看到我啦?!v-else-if > 0.25
</h1>
<h1 v-else>
看到我啦?!v-else
</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
random: 1
}
})
</script>
6、计算属性和侦听器
1、计算属性(computed)
某些结果是基于之前数据实时计算出来的,我们可以利用计算属性。来完成
示例:
<div id="app">
<ul>
<li>西游记:价格{{xyjPrice}},数量:
<input type="number" v-model="xyjNum">
</li>
<li>水浒传:价格{{shzPrice}},数量:
<input type="number" v-model="shzNum">
</li>
<li>总价:{{totalPrice}}</li>
</ul>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
xyjPrice: 56.73,
shzPrice: 47.98,
xyjNum: 1,
shzNum: 1
},
computed: {
totalPrice() {
return this.xyjPrice * this.xyjNum + this.shzPrice * this.shzNum;
}
},
})
</script>

2、侦听(watch)
watch 可以让我们监控一个值的变化。从而做出相应的反应。
示例:
<div id="app">
<ul>
<li>西游记:价格{{xyjPrice}},数量:
<input type="number" v-model="xyjNum">
</li>
<li>水浒传:价格{{shzPrice}},数量:
<input type="number" v-model="shzNum">
</li>
<li>总价:{{totalPrice}}</li>
{{msg}}
</ul>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
xyjPrice: 56.73,
shzPrice: 47.98,
xyjNum: 1,
shzNum: 1,
msg: ""
},
computed: {
totalPrice() {
return this.xyjPrice * this.xyjNum + this.shzPrice * this.shzNum;
}
},
watch: {
xyjNum(newVal, oldVal) {
if (newVal >= 3) {
this.msg = "西游记没有更多库存了";
this.xyjNum = 3;
} else {
this.msg = "";
}
}
}
})
</script>

3、过滤器(filters)

<body>
<div id="app">
<table>
<tr v-for="user in userList">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<!-- 使用代码块实现,有代码侵入-->
<td>{{user.gender===1? "男":"女"}}</td>
</tr>
</table>
</div>
</body>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
userList: [
{ id: 1, name: 'jacky', gender: 1 },
{ id: 2, name: 'peter', gender: 0 }
]
}
});
</script>
1、局部过滤器
注册在当前vue 实例中,只有当前实例能用
let app = new Vue({
el: "#app",
data: {
userList: [
{ id: 1, name: 'jacky', gender: 1 },
{ id: 2, name: 'peter', gender: 0 }
]
},
// filters 定义局部过滤器,只可以在当前vue 实例中使用
filters: {
genderFilter(gender) {
return gender === 1 ? '男~' : '女~'
}
}
});

2、全局过滤器

7、组件化
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。
但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以我们会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。在vue 里,所有的vue 实例都是组件。

1、全局组件
我们通过Vue 的component 方法来定义一个全局组件。
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
// 定义全局组件,两个参数:1,组件名称。2,组件参数
Vue.component("counter", {
template: '<button v-on:click="count++">你点了我{{ count }} 次,我记住了.</button>',
data() {
return {
count: 0
}
}
})
let app = new Vue({
el: "#app"
})
</script>

2、组件的复用
定义好的组件,可以任意复用多次:
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>

3、局部组件
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue 的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。
我们先在外部定义一个对象,结构与创建组件时传递的第二个参数一致:


8、生命周期钩子函数
1、生命周期
每个Vue 实例在被创建时都要经过一系列的初始化过程:创建实例,装载模板,渲染模板等等。Vue 为生命周期中的每个状态都设置了钩子函数(监听函数)。每当Vue 实例处于不同的生命周期时,对应的函数就会被触发调用。
生命周期:你不需要立马弄明白所有的东西。

2、钩子函数

示例
<body>
<div id="app">
<span id="num">{{num}}</span>
<button v-on:click="num++">赞!</button>
<h2>
{{name}},非常帅!!!有{{num}}个人点赞。
</h2>
</div>
</body>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
name: "张三",
num: 100
},
methods: {
show() {
return this.name;
},
add() {
this.num++;
}
},
beforeCreate() {
console.log("=========beforeCreate=============");
console.log("数据模型未加载:" + this.name, this.num);
console.log("方法未加载:" + this.show());
console.log("html 模板未加载:" + document.getElementById("num"));
},
created: function () {
console.log("=========created=============");
console.log("数据模型已加载:" + this.name, this.num);
console.log("方法已加载:" + this.show());
console.log("html 模板已加载:" + document.getElementById("num"));
console.log("html 模板未渲染:" + document.getElementById("num").innerText);
},
beforeMount() {
console.log("=========beforeMount=============");
console.log("html 模板未渲染:" + document.getElementById("num").innerText);
},
mounted() {
console.log("=========mounted=============");
console.log("html 模板已渲染:" + document.getElementById("num").innerText);
},
beforeUpdate() {
console.log("=========beforeUpdate=============");
console.log("数据模型已更新:" + this.num);
console.log("html 模板未更新:" + document.getElementById("num").innerText);
},
updated() {
console.log("=========updated=============");
console.log("数据模型已更新:" + this.num);
console.log("html 模板已更新:" + document.getElementById("num").innerText);
}
});
</script>
</body>

9、vue 模块化开发
1、npm install webpack -g
全局安装webpack
2、npm install -g @vue/cli-init
全局安装vue 脚手架
3、初始化vue 项目
vue init webpack appname:vue 脚手架使用webpack 模板初始化一个appname 项目


4、启动vue 项目;


项目的package.json 中有scripts,代表我们能运行的命令
npm start = npm run dev:启动项目
npm run build:将项目打包
5、模块化开发
1、项目结构


2、Vue 单文件组件
Vue 单文件组件模板有三个部分;

3、vscode 添加用户代码片段(快速生成vue 模板)

{
"生成vue 模板": {
"prefix": "vue",
"body": [
"<template>",
"<div></div>",
"</template>",
"",
"<script>",
"//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json
文件,图片文件等等)",
"//例如:import 《组件名称》from '《组件路径》';",
"",
"export default {",
"//import 引入的组件需要注入到对象中才能使用",
"components: {},",
"props: {},",
"data() {",
"//这里存放数据",
"return {",
"",
"};",
"},",
"//计算属性类似于data 概念",
"computed: {},",
"//监控data 中的数据变化",
"watch: {},",
"//方法集合",
"methods: {",
"",
"},",
"//生命周期- 创建完成(可以访问当前this 实例)",
"created() {",
"",
"},",
"//生命周期- 挂载完成(可以访问DOM 元素)",
"mounted() {",
"",
"},",
"beforeCreate() {}, //生命周期- 创建之前",
"beforeMount() {}, //生命周期- 挂载之前",
"beforeUpdate() {}, //生命周期- 更新之前",
"updated() {}, //生命周期- 更新之后",
"beforeDestroy() {}, //生命周期- 销毁之前",
"destroyed() {}, //生命周期- 销毁完成",
"activated() {}, //如果页面有keep-alive 缓存功能,这个函数会触发
",
"}",
"</script>",
"<style lang='scss' scoped>",
"//@import url($3); 引入公共css 类",
"$4",
"</style>"
],
"description": "生成vue 模板"
}
}
4、导入element-ui 快速开发
- 安装element-ui: npm i element-ui
- 在main.js 中引入element-ui 就可以全局使用了。
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
Vue.use(ElementUI) - 将App.vue 改为element-ui 中的后台布局
- 添加测试路由、组件,测试跳转逻辑
(1) 、参照文档el-menu 添加router 属性
(2) 、参照文档el-menu-item 指定index 需要跳转的地址
updated() {}, //生命周期- 更新之后
beforeDestroy() {}, //生命周期- 销毁之前
destroyed() {}, //生命周期- 销毁完成
activated() {}, //如果页面有keep-alive 缓存功能,这个函数会触发
};
至此商品服务告一段落。
---
## 6.2 品牌管理
这次要用到的代码是通过renren-generator代码生成器中生成的前端代码。在前面中如果我们不小心进行删除了,可以通过idea自带的恢复功能进行恢复。
步骤:
1. 右键点击resources->Local History->Show History

2. 找到删除前端的记录
3. 右键->Revert。 找回成功!

### 6.2.1 使用逆向工程前端代码
1. 菜单管理---新增菜单

2. 将gulimall-product中的前端代码复制到前端工程product下。

3. 没有新增删除按钮: 修改权限,Ctrl+Shift+F查找`isAuth`,全部返回为true


4. 查看效果

这里提一嘴,我们可以将es6语法检查关闭。

### 6.2.2 效果优化-快速显示开关
1. 在列表中添加自定义列:中间加<template></template>标签。可以通过 Scoped slot 可以获取到 row, column, $index 和 store(table 内部的状态管理)的数据
2. 修改开关状态,发送修改请求
3. 数据库中showStatus是01,开关默认值是true/false。 所以在开关中设置:active-value="1" 、:inactive-value="0"属性,与数据库同步


```vue
<!--brand.vue中显示状态那一列-->
<el-table-column
prop="showStatus"
header-align="center"
align="center"
label="显示状态"
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
@change="updateBrandStatus(scope.row)"
>
</el-switch>
</template>
</el-table-column>
<!--brand-add-or-update.vue中显示状态那一列-->
<el-form-item label="显示状态" prop="showStatus">
<el-switch
v-model="dataForm.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
//brand.vue中新增方法,用来修改状态
updateBrandStatus(data) {
let { brandId, showStatus } = data;
this.$http({
url: this.$http.adornUrl("/product/brand/update"),
method: "post",
data: this.$http.adornData({ brandId, showStatus }, false),
}).then(({ data }) => {
this.$message({
message: "状态修改成功",
type: "success",
});
});
},
本文档是谷粒商城项目分布式基础的详细笔记,涵盖了项目简介、分布式基础概念,以及环境搭建的全过程。项目采用B2C电商模式,讲解了微服务、集群、远程调用、负载均衡等概念,并深入介绍了服务注册/发现、配置中心、服务熔断&服务降级等。此外,还详述了Linux虚拟机的安装、Docker环境配置、MySQL与Redis的安装与使用,以及开发环境的统一,包括Maven、Idea&VsCode、Git的配置。文章最后探讨了SpringCloud Alibaba-Nacos作为注册中心和配置中心的使用,并展示了微服务间Feign的远程调用。

771

被折叠的 条评论
为什么被折叠?



