谷粒商城-分布式基础篇[环境准备]
分布式基础篇[环境准备]1、分布式基础概念微服务、注册中心、配置中心、远程调用、Feign、网关2、基础开发SpringBoot2.0、SpringCloud、MyBatis-Plus、Vue组件化、阿里云对象存储3、环境Vagrant、Linux、Docker、MySQL、Redis、逆向工程&人人开源4、开发规范数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理枚举状态、业
- 谷粒商城-分布式基础篇【环境准备】
- 谷粒商城-分布式基础【业务编写】
- 谷粒商城-分布式高级篇【业务编写】持续更新
- 谷粒商城-分布式高级篇-ElasticSearch
- 谷粒商城-分布式高级篇-分布式锁与缓存
- 项目托管于gitee
分布式基础篇[环境准备]
1、分布式基础概念
- 微服务、注册中心、配置中心、远程调用、Feign、网关
2、基础开发
- SpringBoot2.0、SpringCloud、MyBatis-Plus、Vue组件化、阿里云对象存储
3、环境
- Vagrant、Linux、Docker、MySQL、Redis、逆向工程&人人开源
4、开发规范
- 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理
- 枚举状态、业务状态码、VO与TO与PO划分、逻辑删除
- Lombok: @Data、@Slf4j
一、项目简介
1.1、电商模式
市面上有5种常见的电商模式 : B2B、B2C、C2B、C2C、O2O
1.1.1、B2B 模式
B2B (Business to Business), 是指商家和商家建立的商业关系, 如阿里巴巴
1.1.2、B2C 模式
B2C (Business to Consumer) , 就是我们经常看到的供应商直接把商品卖给用户, 即 “商对客” 模式, 也就是我们所说的商业零售, 直接面向消费销售产品和服务, 如苏宁易购、京东、天猫 和 小米商城.
1.1.3、C2B 模式
C2B (Customer to Business) , 即消费者对企业, 先有消费者需求产生 而后有企业生产,
即先有消费者提出需求, 后又生产企业按需求组织生产
1.1.4、C2C 模式
C2C (Customer to Consumer) 客户之间把自己的东西放到网上去卖 .
如 淘宝、咸鱼
1.1.5、O2O 模式
O2O 即 Online To Offline, 也即将线下商务的机会与互联网结合在一起, 让互联网成为线上交易前台, 线上快速支付, 线下优质服务
如 : 饿了么、美团、淘票票、京东到家
1.1.5、谷粒商城
谷粒商城是一个B2C模式的电商平台,销售自营商品给客户 类似“京东”
1.2、项目架构图
1.2.1、项目微服务架构图
前后分离开发,分为内网部署和外网部署,外网是面向公众访问的。访问前端项目,可以有手机APP,电脑网页;内网部署的是后端集群,前端在页面上操作发送请求到后端,在这途中会经过Nginx集群,Nginx把请求转交给API网关(springcloud gateway)(网关可以根据当前请求动态地路由到指定的服务,看当前请求是想调用商品服务还是购物车服务还是检索服务),从路由过来如果请求很多,可以负载均衡地调用商品服务器中一台(商品服务复制了多份),当商品服务器出现问题也可以在网关层面对服务进行熔断或降级(使用阿里的sentinel组件),网关还有其他的功能如认证授权、限流(只放行部分到服务器)等。
到达服务器后进行处理(springboot为微服务),服务与服务可能会相互 调用(使用feign组件),有些请求可能经过登录才能进(基于OAuth2.0的 认证中心。安全和权限使用springSecurity控制)
服务可能保存了一些数据或者需要使用缓存,我们使用redis集群(分片+哨兵集 群)。持久化使用mysql,读写分离和分库分表。、
服务和服务之间会使用消息队列(RabbitMQ),来完成异步解耦,分布式事务 的一致性。有些服务可能需要全文检索,检索商品信息,使用ElaticSearch。
服务可能需要存取数据,使用阿里云的对象存储服务OSS。
项目上线后为了快速定位问题,使用ELK对日志进行处理,使用LogStash收 集业务里的各种日志,把日志存储到ES中,用Kibana可视化页面从ES中检 索出相关信息,帮助我们快速定位问题所在。
在分布式系统中,由于我们每个服务都可能部署在很多台机器,服务和服务 可能相互调用,就得知道彼此都在哪里,所以需要将所有服务都注册到注册中心。服务从注册中心发现其他服务所在位置(使用阿里Nacos作为注册中心)
每个服务的配置众多,为了实现改一处配置相同配置就同步更改,就需要配置中心,也使用阿里的Nacos,服务从配置中心中动态取配置。
服务追踪,追踪服务调用链哪里出现问题,使用springcloud提供的Sleuth、Zipkin、Metrics,把每个服务的信息交给开源的Prometheus进行聚合分析,再由Grafana进行可视化展示,提供Prometheus提供的AlterManager实时得到服务的警告信息,以短信/邮件的方式告知服务开发人员。
还提供了持续集成和持续部署。项目发布起来后,因为微服务众多,每一个都打包部署到服务器太麻烦,有了持续集成后开发人员可以将修改后的代码提交到github,运维人员可以通过自动化工具Jenkins Pipeline将github中获取的代码打包成docker镜像,最终是由k8s集成docker服务,将服务以docker容器的方式运行。
1.2.2、微服务划分图
本图 反映了需要创建的微服务以及相关技术。
前后分离开发。前端项目分为admin-vue(工作人员使用的后台管理系统)、 shop-vue(面向公众访问的web网站)、app(公众)、小程序(公众)
-
商品服务:商品的增删改查、商品的上下架、商品详情
-
支付服务
-
优惠服务
-
用户服务:用户的个人中心、收货地址
-
仓储服务:商品的库存
-
秒杀服务
-
订单服务:订单增删改查
-
检索服务:商品的检索ES
-
中央认证服务:登录、注册、单点登录、社交登录
-
购物车服务
-
后台管理系统:添加优惠信息等
二、分布式基础概念
2.1、微服务
微服务架构风格,就像是把一个单独的应用程序开发成一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API 这些服务围绕业务能力来构建, 并通过完全自动化部署机制来独立部署,这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理
简而言之,拒绝大型单体应用,基于业务边界进行服务微化拆分,每个服务独立部署运行。
2.2、集群、分布式、节点
集群是个物理状态,分布式是个工作方式
《分布式系统原理与范型》定义:
分布式系统是若干独立计算机的集合,这些计算机对于用户来说像单个系统 分布式系统 (distributed system) 是建立网络之上的软件系统.
- 分布式是指根据不同的业务分布在不同的地方
- 集群指的是将几台服务器集中在一起,实现同一业务
- 节点:集群中的一个服务器
分布式中的每一个节点,都可以做集群,而集群并不一定就是分布式的
例如:京东是一个分布式系统,众多业务运行在不同的机器上,所有业务构成 一个大型的分布式业务集群,每一个小的业务,比如用户系统,访问压力大的 时候一台服务器是不够的,我们就应该将用户系统部署到多个服务器,也就是 每一个业务系统也可以做集群化
2.3、远程调用
在分布式系统中, 各个服务可能处于不同主机, 但是服务之间不可避免的需要互相协调, 我们称之为 远程调用
SpringCloud中使用HTTP+JSON的方式来完成远程调用
2.4、负载均衡
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高用)。
分布式系统中, A 服务需要调用 B服务, B服务部署在多台服务器上, A调用任意一个服务器均可完成功能. 为了使每一个服务器都不要太闲或者太忙, 我们可以使用负载均衡调用集群中的每一个服务器, 从而达到系统的高可用 .
常见的负载均衡算法 :
- 轮训算法 : 为第一个请求选择健康池中的每一个服务器, 然后按顺序往后依次选择, 直到最后一个, 然后循环
- 优先选择链接数最少,也就是压力最小的后端服务器,在会话较 长的情况下可以考虑采取这种方式
2.5、服务注册/发现 、注册中心
A服务调用B服务,A服务不知道B服务当前在哪几台服务器上有,哪些正常的,哪些服务已经下线,解决这个问题可以引入注册中心. 如果某些服务下线,我们其他人可以实时的感知到其他服务的状态, 从而避免调用不可用的服务
2.6、配置中心
每一个服务最终都有大量配置,并且每个服务都可能部署在多个服务器上,我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。
配置中心用来集中管理微服务的配置信息
Spring Cloud Config为分布式系统中的外部配置提供服务器和客户端支持
2.7、服务熔断、服务降级
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免失败 .
服务雪崩:
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某个微服务的调用响应时间过长,或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几十秒内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以达到单个依赖关系的失败而不影响整个应用程序或系统运行。
我们需要,弃车保帅!
2.7.1、服务熔断
设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保 护机制,后来的请求不再去调用这个服务,本地直接返回默认的数据
熔断机制是赌赢雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是:@HystrixCommand。
服务熔断解决如下问题:
- 当所依赖的对象不稳定时,能够起到快速失败的目的;
- 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。
2.7.2、服务降级
在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级 运行,降级:某些服务不处理,或者简单处理【抛异常,返回NULL,调用 Mock数据,调用 FallBack 处理逻辑】
服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
降级的方式可以根据业务来,可以延迟服务,比如延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行 ;或者在粒度范围内关闭服务,比如关闭相关文章的推荐。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bY8YoZS3-1648044754328)(/Users/hgw/Documents/hgw/note/谷粒商城.assets/服务降级.png)]
由上图可得,当某一时间内服务A的访问量暴增,而B和C的访问量较少,为了缓解A服务的压力,这时候需要B和C暂时关闭一些服务功能,去承担A的部分服务,从而为A分担压力,叫做服务降级。
2.8、API 网关
在微服务架构中,API Gateway 作为整体架构的重要组件,抽象服务中需要的公共功能,同时它提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流监控,日志统计等丰富功能,帮助我们解决很多API管理的难题
三、环境安装
3.1、安装jdk1.8
[root@hgwtencent software]# ll
总用量 112620
-rw-r--r-- 1 root root 115315748 3月 4 21:11 jdk-8u311-linux-x64.rpm
[root@hgwtencent software]# rpm -ivh jdk-8u311-linux-x64.rpm
警告:jdk-8u311-linux-x64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID ec551f03: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:jdk1.8-2000:1.8.0_311-fcs ################################# [100%]
Unpacking JAR files...
tools.jar...
plugin.jar...
javaws.jar...
deploy.jar...
rt.jar...
jsse.jar...
charsets.jar...
localedata.jar...
[root@hgwtencent software]# vim /etc/profile
[root@hgwtencent software]# source /etc/profile
[root@hgwtencent software]# java -version
java version "1.8.0_311"
Java(TM) SE Runtime Environment (build 1.8.0_311-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.311-b11, mixed mode)
配置环境变量, vim /etc/profile
JAVA_HOME=/usr/java/jdk1.8.0_311-amd64
CLASSPATH=%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
export PATH CLASSPATH JAVA_HOME
3.2、安装Docker
1. yum安装gcc相关(需要确保 虚拟机可以上外网 )
[root@HgwCloudServer home]# yum -y install gcc
[root@HgwCloudServer home]# yum -y install gcc-c++
2. 卸载就版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
3. 安装需要的软件包
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
4. 设置stable镜像仓库, 官网的镜像是国外的,这里推荐使用阿里的
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
5. 更新yum软件包索引
yum makecache fast
6. 安装Docker CE(社区版本) EE(企业版)
yum -y install docker-ce docker-ce-cli containerd.io
7. 启动docker
systemctl enable docker && systemctl start docker
8. 配置阿里云镜像加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://5yvx8fu0.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
9. 测试
docker version # 查看是否安装成功
docker run hello-world # 运行 hello-world
docker images # 查看下载的docker镜像
3.3、安装mysql
1.拉取镜像
[root@hgwtencent ~]# docker pull mysql:5.7
2.启动mysql容器
[root@hgwtencent ~]# sudo 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
# --name指定容器名字 -v目录挂载 -p指定端口映射 -e设置mysql参数 -d后台运行
[root@hgwtencent ~]# vim
本地工具远程连接 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9u8vFHYE-1648045344424)(
mysql配置 :
vim /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
3.4、安装Redis
1 在docker hub搜索redis镜像
[root@hgwtencent mydata]# docker search redis
2 拉取redis镜像到本地
[root@hgwtencent mydata]# docker pull redis:6.0.10
3. 修改需要自定义的配置(docker-redis默认没有配置文件,自己在宿主机建立后挂载映射)
[root@hgwtencent mydata]# mkdir -p /mydata/redis/conf
[root@hgwtencent mydata]# touch /mydata/redis/conf/redis.conf
---
创建并修改 mydata/redis/conf/redis.conf
bind 0.0.0.0 #开启远程权限
appendonly yes #开启aof持久化
----
4. 启动redis服务运行容器
[root@hgwtencent mydata]# docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf -d redis:6.0.10 redis-server /etc/redis/redis.conf
e4e58d5a93884d6ee85a23e05c7d69a8792cad8b42dec1fc4b59ef76963f5996
解释:
-v /usr/local/redis/data:/data # 将数据目录挂在到本地保证数据安全
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf # 将配置文件挂在到本地修改方便
5. 测试
[root@hgwtencent conf]# docker exec -it redis redis-cli
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> get a
"b"
127.0.0.1:6379> exit
3.5、配置Maven
在maven配置文件配置
- 配置阿里云镜像
<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>
- 配置 jdk 1.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.6、安装开发插件
idea工具
-
配置maven
-
下载
lombok
插件、mybatisX
插件
vscode 工具
- Auto Close Tag
- Auto Rename Tag
- Chinese
- ESlint
- HTML CSS Support
- HTML Snippets
- JavaScript (ES6) code snippets
- Live Server
- open in brower
- Vetur
3.7、安装配置git
安装配置git
#配置用户名
git config --global user.name "username" //(名字,随意写)
#配置邮箱
git config --global user.email "gitee上注册的邮箱@xx.com" // 注册账号时使用的邮箱
#配置ssh免密登录
ssh-keygen -t rsa -C "gitee上注册的邮箱@xx.com"
三次回车后生成了密钥,也可以查看密钥
cat ~/.ssh/id_rsa.pub
浏览器登录码云后,个人头像上点设置、然后点ssh公钥、随便填个标题,然后赋值
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDTrbyiMS692ehic18FpiItcAGGUqT+8UZLzcv6sMG5yFgMqcqt11w6SOeERZ+EklQ+z1GeouoXhqXv2x4JtnBu5A0bvF/hvAUGZ0OzjjdE6pxxNBipNqeGms4UqDo2RMMTbhH/rpwqxZGMNZfrdt0wsg+aIcLAQHSnrv5rpb1SD9Hv/U5wynxReWh3KfV+c/pqAbVz1Q21ua2qwPeMoTNAjkJZYaPTVGKkOlHcDFzaEdISYGpgClFbPEvWUZXaZeWYKEICYAfwjAvx2L/i/jXxiRYuUdnAfFoBRnabtC9j3TsgAAw71H3wwQLhGYEQA5m3o1wA/0TrKTFegFV7GgF53b9+A/28zFZQoYKjMMDf0LOPFunU77YD59ZwG1rPe4lWUfHJ9+lABq46GcPIwxEflnDAtpScUCbXbyIhUubr3qLiIohBUidgBoK9ztaGBA4ZoU/5pYs8JIYJnpv0g2CFd20FRrNpWQd4EiJSZJBrDXkzC2aeOAz0a+V4tjZS/FU= macbookari
#测试
ssh -T git@gitee.com
#测试成功
Hi HGW689! You've successfully authenticated, but GITEE.COM does not provide shell access.
3.8、创建仓库
在码云新建仓库,仓库名gulimall,选择语言java,在.gitignore选中maven, 许可证选Apache-2.0,开发模型选生产/开发模型,开发时在dev分支, 发布时在master分支,创建如图所示
3.9、新建项目并创建出以下服务模块
第一步、在IDEA中New Project from version control Git 复制刚才项目的地址,如https://gitee.com/yxj/gulimall.git
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYjIVMW7-1648045482013)(
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tsw89IXt-1648045518636)(
创建以下模块 :
- 商品服务product
- 存储服务ware
- 订单服务order
- 优惠券服务coupon
- 用户服务member
各个模块的共同点 :
-
每个服务都先导入
web
、openfeign
-
每一个服务, 包名 :
com.hgw.gulimall.xxx(producer/order/ware/coupon/member)
-
模块名 :
gulimall-coupon
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tb0twVsU-1648045571355)(
如下图所示 :
商品服务product
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPtiWYWY-1648085060022)(
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ci1ZiCW-1648085108484)(
3.10、项目初始化 并 上传马云
创建父模块:在gulimall中创建pom.xml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KuLlWSoz-1648085229698)(
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hgw.gulimall</groupId>
<artifactId>gulimall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gulimall</name>
<description>聚合服务</description>
<packaging>pom</packaging>
<modules>
<module>gulimall-coupon</module>
<module>gulimall-member</module>
<module>gulimall-order</module>
<module>gulimall-product</module>
<module>gulimall-ware</module>
</modules>
</project>
在maven窗口刷新,并点击+号,找到刚才的pom.xml添加进来,发现多了个root。这样比如运行root的clean命令,其他项目也一起clean了。
修改总项目的.gitignore,把小项目里的垃圾文件在提交的时候忽略掉
3.11、数据库初始化
通过建模语言构建项目数据库, 导出sql. 然后通过数据库可视化工具创建相应的database
并分别执行其对应的.sql文件
创建数据库时在左侧root上右键建立数据库: 字符集选utf8mb4,他能兼容utf8且能解决一些乱码的问题。分别 建立了下面数据库
-
gulimall_oms
-
gulimall_pms
-
gulimall_sms
-
gulimall_ums
-
gulimall_wms
所有的数据库数据再复杂也不建立外键,因为在电商系统里,数据量大, 做外键关联很耗性能。
gulimall_oms
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 '退款信息';
gulimall_pms
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-需要]',
icon varchar(255) comment '属性图标',
value_select char(255) comment '可选值列表[用逗号分隔]',
attr_type tinyint 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信息介绍';
gualmill_sms
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积分设置';
gulimall_ums
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 '会员统计信息';
u
gulimall_wms
/*
SQLyog Ultimate v11.25 (64 bit)
MySQL - 5.7.27 : Database - gulimall_wms
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`gulimall_wms` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `gulimall_wms`;
/*Table structure for table `undo_log` */
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `undo_log` */
/*Table structure for table `wms_purchase` */
DROP TABLE IF EXISTS `wms_purchase`;
CREATE TABLE `wms_purchase` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`assignee_id` bigint(20) DEFAULT NULL,
`assignee_name` varchar(255) DEFAULT NULL,
`phone` char(13) DEFAULT NULL,
`priority` int(4) DEFAULT NULL,
`status` int(4) DEFAULT NULL,
`ware_id` bigint(20) DEFAULT NULL,
`amount` decimal(18,4) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购信息';
/*Data for the table `wms_purchase` */
/*Table structure for table `wms_purchase_detail` */
DROP TABLE IF EXISTS `wms_purchase_detail`;
CREATE TABLE `wms_purchase_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`purchase_id` bigint(20) DEFAULT NULL COMMENT '采购单id',
`sku_id` bigint(20) DEFAULT NULL COMMENT '采购商品id',
`sku_num` int(11) DEFAULT NULL COMMENT '采购数量',
`sku_price` decimal(18,4) DEFAULT NULL COMMENT '采购金额',
`ware_id` bigint(20) DEFAULT NULL COMMENT '仓库id',
`status` int(11) DEFAULT NULL COMMENT '状态[0新建,1已分配,2正在采购,3已完成,4采购失败]',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*Data for the table `wms_purchase_detail` */
/*Table structure for table `wms_ware_info` */
DROP TABLE IF EXISTS `wms_ware_info`;
CREATE TABLE `wms_ware_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) DEFAULT NULL COMMENT '仓库名',
`address` varchar(255) DEFAULT NULL COMMENT '仓库地址',
`areacode` varchar(20) DEFAULT NULL COMMENT '区域编码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='仓库信息';
/*Data for the table `wms_ware_info` */
/*Table structure for table `wms_ware_order_task` */
DROP TABLE IF EXISTS `wms_ware_order_task`;
CREATE TABLE `wms_ware_order_task` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`order_id` bigint(20) DEFAULT NULL COMMENT 'order_id',
`order_sn` varchar(255) DEFAULT NULL COMMENT 'order_sn',
`consignee` varchar(100) DEFAULT NULL COMMENT '收货人',
`consignee_tel` char(15) DEFAULT NULL COMMENT '收货人电话',
`delivery_address` varchar(500) DEFAULT NULL COMMENT '配送地址',
`order_comment` varchar(200) DEFAULT NULL COMMENT '订单备注',
`payment_way` tinyint(1) DEFAULT NULL COMMENT '付款方式【 1:在线付款 2:货到付款】',
`task_status` tinyint(2) DEFAULT NULL COMMENT '任务状态',
`order_body` varchar(255) DEFAULT NULL COMMENT '订单描述',
`tracking_no` char(30) DEFAULT NULL COMMENT '物流单号',
`create_time` datetime DEFAULT NULL COMMENT 'create_time',
`ware_id` bigint(20) DEFAULT NULL COMMENT '仓库id',
`task_comment` varchar(500) DEFAULT NULL COMMENT '工作单备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存工作单';
/*Data for the table `wms_ware_order_task` */
/*Table structure for table `wms_ware_order_task_detail` */
DROP TABLE IF EXISTS `wms_ware_order_task_detail`;
CREATE TABLE `wms_ware_order_task_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`sku_id` bigint(20) DEFAULT NULL COMMENT 'sku_id',
`sku_name` varchar(255) DEFAULT NULL COMMENT 'sku_name',
`sku_num` int(11) DEFAULT NULL COMMENT '购买个数',
`task_id` bigint(20) DEFAULT NULL COMMENT '工作单id',
`ware_id` bigint(20) DEFAULT NULL COMMENT '仓库id',
`lock_status` int(1) DEFAULT NULL COMMENT '1-已锁定 2-已解锁 3-扣减',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存工作单';
/*Data for the table `wms_ware_order_task_detail` */
/*Table structure for table `wms_ware_sku` */
DROP TABLE IF EXISTS `wms_ware_sku`;
CREATE TABLE `wms_ware_sku` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`sku_id` bigint(20) DEFAULT NULL COMMENT 'sku_id',
`ware_id` bigint(20) DEFAULT NULL COMMENT '仓库id',
`stock` int(11) DEFAULT NULL COMMENT '库存数',
`sku_name` varchar(200) DEFAULT NULL COMMENT 'sku_name',
`stock_locked` int(11) DEFAULT '0' COMMENT '锁定库存',
PRIMARY KEY (`id`),
KEY `sku_id` (`sku_id`) USING BTREE,
KEY `ware_id` (`ware_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品库存';
/*Data for the table `wms_ware_sku` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
3.12、工程搭建
这里使用脚手架 https://gitee.com/renrenio , 在码云上搜索人人开源,我们使用renren-fast,renren-fast-vue项目
下载到了桌面,我们把renren-fast移动到我们的项目文件夹(删掉.git文件),而renren-fast-vue(删掉.git文件)是用VSCode打开的(后面再弄)
第一步、下载脚手架
hgw@HGWdeAir 人人开源 % git clone https://gitee.com/renrenio/renren-fast.git
hgw@HGWdeAir 人人开源 % git clone https://gitee.com/renrenio/renren-fast-vue.git
3.12.1、renren-fast
第二步、把renren-fast移动到我们的项目文件夹(删掉.git文件)
在idea(root)项目里的pom.xml添加一个
<modules>
<module>gulimall-coupon</module>
<module>gulimall-member</module>
<module>gulimall-order</module>
<module>gulimall-product</module>
<module>gulimall-ware</module>
<module>renren-fast</module>
</modules>
第三步、创建
gulimall-admin
数据库
打开项目中/renren-fast/db/mysql.sql
文件, 复制全部内容. 通过可视化工具, 创建库gulimall_admin,粘贴以下的内容执行。
第四步、修改 配置文件
url: jdbc:mysql://124.222.223.222:3306/gulimall_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
然后执行java下的RenrenApplication
浏览器输入http://localhost:8080/renren-fast/
得到 :{“msg”:“invalid token”,“code”:401}就代表无误
3.12.2、renren-fast-vue
第五步、安装 node.js
官网下载地址 : https://nodejs.org/download/release/v14.15.0/
推荐地址: https://registry.npmmirror.com/binary.html?path=node/v10.16.3/
这里没见一个大佬的 https://blog.csdn.net/hancoder/article/details/113821646
前端开发, 少不了 node.js ; node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.
我们关注与 node.js 的 npm 功能就行 ;
NPM 是随同 NodeJs 一起安装的包管理工具, JavaScript-NPM,Java-Mave
跟着步骤一直点即可!
hgw@HGWdeAir ~ % node -v
v14.15.0
配置npm使用淘宝镜像
npm config set registry http://registry.npm.taobao.org/ # 设置node仓库。提高下载速度
在vscode中 终端输入一下命令 :
npm install
npm run dev
浏览器输入localhost:8001 就可以看到内容了,登录账号admin 密码admin
3.13、逆向工程搭建
第一步、在gitee上下载代码生成器
hgw@HGWdeAir 人人开源 % git clone https://gitee.com/renrenio/renren-generator.git
第二步、下载到桌面后,同样把里面的.git文件删除,然后移动到我们IDEA项目目录中,同样配置好pom.xml(root)
第三步、修改配置文件
修改renren-generator的application.yml
url: jdbc:mysql://124.222.223.222/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
修改generator.properties
mainPath=com.hgw # 主目录
#包名
package=com.hgw.gulimall # 包名
moduleName=product # 模块名
#作者
author=hgw # 作者
#Email
email=hgw6721@163.com # email
#表前缀(类名不会包含表前缀)
tablePrefix=pms_ # 我们的pms数据库中的表的前缀都有pms,如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
修改逆向工程的模板 : /Users/hgw/Documents/Data/java/gulimall/renren-generator/src/main/resources/template/Controller.java.vm
文件
- 删掉
import org.apache.shiro.authz.annotation.RequiresPermissions;
引入 - 并注释所有方法上的
@RequiresPermissions("${moduleName}:${pathName}:list")
注解
第四步、运行RenrenApplication。如果启动不成功,修改application中是port为80。访问http://localhost:80, 并生成代码
第五步、解压压缩包,把main放到gulimall-product的同级目录下。
第六步、创建
gulimall-common
格式为 maven的模块, 并在项目的pom.xml中也自动添加了<module>gulimall-common</module>
, 用来存放每一个微服务公共的依赖、bean、工具类等
项目的pom.xml :
<modules>
<module>gulimall-common</module>
<module>gulimall-coupon</module>
<module>gulimall-member</module>
<module>gulimall-order</module>
<module>gulimall-product</module>
<module>gulimall-ware</module>
<module>renren-fast</module>
<module>renren-generator</module>
<module>gulimall-common</module>
</modules>
第七步、配置
gulimall-common
模块
配置依赖配置文件 pom.xml :
<dependencies>
<!--MyBatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--简化实体类,用@Data代替getset方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!-- httpcomponent包https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
在gulimall-common
模块下创建以下类, 从 gulimall/renren-fast/src/main/java/io/renren/common
下的寻找如下类复制过来即可 .
第七步、修改各微服务的pom.xml文件, 插入依赖
<dependency>
<groupId>com.hgw.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
3.14、整合MyBatisPlus
3.14.1、导入依赖
<!--MyBatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
3.14.2、配置文件
配置数据源
第一步、导入数据库的驱动
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
第二步、在application.yml配置相关数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_pms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
3.14.3、配置 MyBatisPlus
配置 MyBatisPlus
第一步、通过@MapperScan注解 指定扫描文件位置
@MapperScan("com/hgw/gulimall/product/dao") // 扫描的我们的mapper文件夹
@SpringBootApplication
public class GulimallProductApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallProductApplication.class, args);
}
}
第二步、告诉MyBatis-Plus,sql映射文件位置
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
3.14.4、测试
测试插入
package com.hgw.gulimall.product;
import com.hgw.gulimall.product.entity.BrandEntity;
import com.hgw.gulimall.product.service.BrandService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class GulimallProductApplicationTests {
@Autowired
BrandService brandService;
@Test
void contextLoads() {
BrandEntity brandEntity = new BrandEntity();
brandEntity.setName("华为");
brandEntity.setDescript("中华有为");
boolean save = brandService.save(brandEntity);
System.out.println((save?"保存成功!":"保存失败!"));
}
}
3.15、逆向工程生成所有所有微服务基本CRUD代码
3.15.1、生成 gulimall-coupon
(优惠服务)微服务基本代码
生成
gulimall-coupon
(优惠服务)微服务基本代码
-
修改
renren-generator
微服务下的/src/main/resources/generator.properties
文件的 ==moduleName
==属性 和 ==tablePrefix
==属性mainPath=com.hgw #包名 package=com.hgw.gulimall moduleName=coupon #作者 author=hgw #Email email=hgw6721@163.com #表前缀(类名不会包含表前缀) 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了 tablePrefix=sms_
-
修改
renren-generator
微服务下的/src/main/resources/application.yml
文件 的 url 属性url: jdbc:mysql://124.222.223.222/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
运行下载代码
下载代码解压到相应微服务中, 修改其配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_sms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
server:
port: 8000
3.15.2、生成 gulimall-member
(会员服务)微服务基本代码
generator.properties
# 主目录
mainPath=com.hgw
#包名
package=com.hgw.gulimall
moduleName=member
#作者
author=hgw
#Email
email=hgw6721@163.com
#表前缀(类名不会包含表前缀) 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
tablePrefix=ums_
application.yml
url: jdbc:mysql://124.222.223.222/gulimall_ums?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
gulimall-member 的 application.yml :
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_sms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
server:
port: 7000
3.15.3、生成 gulimall-order
(订单服务)微服务基本代码
generator.properties
# 主目录
mainPath=com.hgw
#包名
package=com.hgw.gulimall
moduleName=order
#作者
author=hgw
#Email
email=hgw6721@163.com
#表前缀(类名不会包含表前缀) 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
tablePrefix=oms_
application.yml
url: jdbc:mysql://124.222.223.222/gulimall_oms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
gulimall-member 的 application.yml :
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_oms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
server:
port: 9000
3.15.4、生成 gulimall-ware
(库存服务)微服务基本代码
generator.properties
# 主目录
mainPath=com.hgw
#包名
package=com.hgw.gulimall
moduleName=ware
#作者
author=hgw
#Email
email=hgw6721@163.com
#表前缀(类名不会包含表前缀) 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
tablePrefix=wms_
application.yml
url: jdbc:mysql://124.222.223.222/gulimall_wms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
gulimall-member 的 application.yml :
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_wms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
server:
port: 11000
3.16、SpringBoot降级以及Junit报错处理
坚持少bug, 保持和老师同一版本原则, 现进行降版本处理 :
这里从2.6.4
降到 2.1.8
第一步、修改pom.xml文件, 并刷新maven, 再clean
此时Test类已报错 !
第二步、Junit报错处理
- 删掉
import org.junit.jupiter.api.Test;
- 导入
import org.junit.Test;
包 ; - 在类上
@RunWith(SpringRunner.class)
注解 - 给类 和 方法加上
public
四、SpringCloud Alibaba 简介
- SpringCloud Alibaba-Nacos : 注册中心 (服务发现/注册)
- SpringCloud Alibaba-Nacos : 配置中心 (动态配置管理)
- SpringCloud-Ribbon : 负载均衡
- SpringCloud-Feign : 声明式HTTP客户端 (调用远程服务)
- SpringCloud Alibaba-Sentinel : 服务容错 (限流、降级、熔断)
- SpringCloud-Gateway : API网关 (web flux 编程模式)
- SpringCloud-Sleuth : 调用链监控
- SpringCloud Alibaba-Seata : 原 Fescar, 即分布式事务解决方案
4.1、搭配环境
在common的pom.xml中加入
# 下面是依赖管理,相当于以后再dependencies里引spring cloud alibaba就不用写版本号, 全用dependencyManagement进行管理
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.2、SpringCloud Alibaba-Nacos [作为注册中心]
一个更易于构建云原生应用的动态服务发现、配置管理 和 服务管理平台 .
作为我们的注册中心 和 配置中心
先了解一下 Spring Cloud 应用如何接入 Nacos Discovery。
4.2.1、配置 SpringCloud Alibaba-Nacos [作为注册中心]
第一步、修改 common中的pom.xml 文件,引入 Nacos Discovery Starter。
第一步、修改 common中的pom.xml 文件,引入 Nacos Discovery Starter。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
第二步、安装并启动 Nacos
4.2.2.1、Docker部署Nacos
这里直接在服务器上Docker部署的Nacos, 并开启相应端口
8848
-
拉取
nacos
镜像docker pull nacos/nacos-server
-
启动
nacos
docker run -d --name nacos -p 8848:8848 -e PREFER_HOST_MODE=hostname -e MODE=standalone nacos/nacos-server
至此,我们已经可以使用nacos服务,UI地址:http://:8848/nacos 账号:nacos 密码:nacos
4.2.2.2、本机部署
本机部署 , 本机存放路径:
/Users/hgw/Documents/Software/nacos/bin
-
下载nacos的压缩包,并解压
https://github.com/alibaba/nacos/releases
-
进入解压目录的bin目录下,打开终端,输入命令启动,输出
nacos is starting with standalone
即为成功/Users/hgw/Documents/Software/nacos/bin
sh startup.sh -m standalone
-
进入可视化页面,账号密码都是nacos,进行登录即可,nacos的端口为8848 (账号:nacos 密码:nacos)
http://localhost:8848/nacos/#/login http://127.0.0.1:8848/nacos/#/login
-
关闭nacos
sh shutdown.sh
-
但发现关闭后,仍然能在可视化页面连接nacos,所以需要杀死8848端口的进程
# 查询8848端口的进程,获取到进程id,例如是45025 lsof -i:8848 # 杀死45025进程 kill -9 45025
第三步、配置 Nacos Server 地址和微服务名称
第三步、在应用的common中的application.yml 配置文件中配置 Nacos Server 地址 和 微服务名称
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://124.222.223.222:3306/gulimall_sms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-coupon
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
# 设置表主键自增
id-type: auto
server:
port: 7000
本人用的是服务器Nacos.
第四步、使用 @EnableDiscoveryClient 注解开启服务注册与发现功能
第四步、使用 @EnableDiscoveryClient 注解开启服务注册与发现功能
启动微服务测试一下 :
总结 : 重复上面第三步、第四步 将所有微服务都加入注册中心
- 在各微服务中的application.yml 配置文件中配置 Nacos Server 地址 和 微服务名称
- 在各微服务的主类加上
@EnableDiscoveryClient
注解开启服务注册与发现功能
4.2.2、测试 member(会员)和coupon(优惠券)的远程调用
想要获取当前会员领取到的所有优惠券。先去注册中心找优惠券服务, 注册中心调一台优惠券服务器给会员,会员服务器发送请求给这台优 惠券服务器,然后对方响应。
Feign与注册中心 spring cloud feign 声明式远程调用 feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。 给远程服务发的是HTTP请求。
一、想要远程调用别的服务
* 1、引入open=feign
* 2、编写一个接口,告诉SpringCloud这个接口需要调用远程服务
* 2.1、声明接口的每一个方法都是调用哪个远程服务的哪个请求
* 3、开启远程调用功能 @EnableFeignClients
4.2.2.1、在 member 和 coupon 中引入openfeign依赖
会员服务想要远程调用优惠券服务,只需要给会员服务里引入openfeign依赖,他就有了远程调用其他服务的能力。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4.2.2.2、在 coupon下的 CouponController
类中加入一个获取优惠券的方法
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@RequestMapping("/member/list")
public R memberCoupons() {
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满100减10");
return R.ok().put("coupons",Arrays.asList(couponEntity));
}
/ ......
}
这样我们准备好了优惠券的调用内容
4.2.2.3、在 member 下编写一个接口,告诉SpringCloud这个接口需要调用远程服务
声明接口的每一个方法都是调用哪个远程服务的哪个请求
那么要调用什么东西呢?就是我们刚才写的优惠券的功能,
在 com.hgw.gulimall.member
下 创建一个 feign
包, 专门用来存放调用远程服务的类
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
@RequestMapping("")
public R memberCoupons();
}
-
@FeignClient("gulimall-coupon")
: 告诉spring cloud这个接口是一个远程客户端,要调用coupon服务 -
@RequestMapping("/coupon/coupon/member/list")
: 再去调用coupon服务/coupon/coupon/member/list
对应的方法 -
public R memberCoupons();
: 得到一个R对象
4.2.2.4、member 开启远程调用功能 @EnableFeignClients
@EnableFeignClients(basePackages = “远程调用的包路径”)
告诉spring这里面是一个远程调用客户端,member要调用的接口
4.2.2.5、在member的控制层写一个测试请求
@RestController
@RequestMapping("member/member")
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
CouponFeignService couponFeignService;
@RequestMapping("/coupons")
public R test() {
MemberEntity memberEntity = new MemberEntity();
memberEntity.setNickname("张三");
R memberCoupons = couponFeignService.memberCoupons();
return R.ok().put("member", memberEntity).put("coupons",memberCoupons.get("coupons"));
}
//......
}
重新启动服务, 访问 http://localhost:8000/member/member/coupons
4.3、SpringCloud Alibaba-Nacos [配置中心]
我们还可以用nacos作为配置中心。配置中心的意思是不在application.properties 等文件中配置了,而是放到nacos配置中心公用,这样无需每台机器都改。
如何使用 Nacos 作为配置中心统一管理配置
- 引入配置中心依赖
- 在
resources
目录下创建一个 bootstrap.properties文件并配置 - 需要给配置中心默认添加一个 数据集 (Data Id) 默认规则:
应用名.properties
- 给
应用名.properties
添加任何配置- 动态获取配置, 给控制层类加上
@RefreshScope
注解, (动态获取并刷新配置) - 属性加上
@Value("${配置项的名}")
注解, (获取到配置)
- 动态获取配置, 给控制层类加上
同时加载多个配置集
- 微服务任何配置信息, 任何配置文件都可以放在配置中心中
- 只需要在
bootstrap.properties
说明加载配置中心中哪些配置文件即可 @Value
、@ConfigurationProperties
… 以前 SpringBoot 任何方法从配置文件中获取值, 都能使用.
配置中心有的优先使用配置中心的
4.3.1、nacos配置
4.3.1.1、nacos配置参数
-
命名空间 :
用作配置隔离 . (一般每个微服务一个命名空间)
不同的命名空间下, 可以存在相同的
Group
或Data ID
的配置.Namespace
的常用场景之一是不同环境的配置区分隔离, 例如 开发测试环境和生产环境的资源 (如配置、服务)隔离等.默认:
public
(保留空间); 默认新增的所有配置都在public
空间- 可以在 开发、测试、生产 利用命名空间来做环境隔离
- 每一个微服务之间互相隔离配置, 每一个微服务都创建自己的命名空间, 只加载自己命名空间下的所有配置
- 创建命名空间
-
在 dev 命名空间下创建
gulimall-coupon.properties
配置文件 -
修改本地微服务的
bootstrap.properties
配置
也可以为每个微服务配置一个命名空间,微服务互相隔离
-
配置集 :
一组相关或不相关的配置项的集合.
在系统中, 一个配置文件通常就是一个配置集, 包含了系统各个方面的配置. 例如, 一个配置集可以包含了数据源、线程池、日志级别等配置项
-
配置集ID :
类似于配置文件名,即Data ID
Data Id
通常用于组织 划分系统的配置集. 一个系统或者应用可以包含多个配置集, 每个配置集都可以被一个有意义的名称标识.Data ID
通常采用类 Java 包 (如 com.hgw.tc.refund.log.level) 的命名规则保证全局唯一性. 此命名非强制 . -
配置分组 :
默认所有的配置集都属于
DEFAULT_GROUP
. 自己可以创建分组, 比如双十一、618、双十二- 新建配置指定分组
- 修改配置文件
4.3.1.2、本项目配置
最终方案 : 每个微服务创建自己的命名空间, 然后使用配置分组区分环境 (dev/test/pord)
第一步、将配置文件分为三部分, 配置分组为
dev
-
datasource.yml
: 用来存放数据库配置spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://124.222.223.222:3306/gulimall_sms?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
-
mybatis.yml
: 用来存放mybatis的配置mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml global-config: db-config: # 设置表主键自增 id-type: auto
-
other.yml
: 存放其他配置spring: cloud: nacos: discovery: server-addr: 124.222.223.222:8848 application: name: gulimall-coupon server: port: 7000
第二步、修改本地
bootstrap.properties
, 并注释掉原application.yml
配置文件中所有内容
# 项目的名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
# 配置中心的地址
spring.cloud.nacos.config.server-addr=124.222.223.222:8848
# 指定命名空间
spring.cloud.nacos.config.namespace=0793dfc5-ec7c-4ead-9747-48fe1bfd535d
# spring.cloud.nacos.config.group=dev # 指定配置分组
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[0].group=dev
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=dev
spring.cloud.nacos.config.ext-config[1].refresh=true
spring.cloud.nacos.config.ext-config[2].data-id=other.yml
spring.cloud.nacos.config.ext-config[2].group=dev
spring.cloud.nacos.config.ext-config[2].refresh=true
4.3.2、配置步骤
第一步、引入配置中心依赖,放到common的pom.xml中
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
第二步、在项目中创建/src/main/resources/bootstrap.properties
bootstrap.properties
这个文件是 springboot里规定的,他优先级别application.properties高
这里以 coupons项目为例
# 项目的名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
# 配置中心的地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
4.3.3、测试
- 编写一个测试请求
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@Value("${coupon.user.name}")//从application.properties中获取//不要写user.name,他是环境里的变量
private String name;
@Value("${coupon.user.age}")
private Integer age;
@RequestMapping("/test")
public R test() {
return new R().put("name",name).put("age",age);
}
// ......
}
- 浏览器去nacos里的配置列表,点击+号,data ID:gulimall-coupon.properties,配置
然后点击发布。重启coupon , 访问 http://localhost:7000/coupon/coupon/test
- 实际生产中不能重启应用。在coupon的控制层上加
@RefreshScope
- 重启后,在nacos浏览器里修改配置,修改就可以观察到能动态修改了
nacos的配置内容优先于项目本地的配置内容。
4.4、Spring Cloud Gateway (网关)
4.4.1、概述
4.4.1.1、网关的定义 :
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
网关的职责 :
- 请求接入 : 作为所有API接口服务请求的接入点
- 业务聚合 : 作为所有后端业务服务的聚合点
- 中介策略 : 实现安全、验证、路由、过滤、控制等策略
- 统一管理 : 对所有API服务和策略进行统一管理
4.4.1.2、Gateway 概述
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
发送请求需要知道商品服务的地址,如果商品服务器有100服务器,1号掉线后,还得改,所以需要网关动态地管理,他能从注册中心中实时地感知某个服务上线还是下线。
请求也要加上询问权限,看用户有没有权限访问这个请求,也需要网关。所以我们使用spring cloud的gateway组件做网关功能。
网关是请求浏览的入口,常用功能包括路由转发,权限校验,限流控制等。springcloud gateway取到了zuul网关。
三大核心概念:
- Route ( 路由 ) : 这是网关的基本构件块. 它由一个ID, 一个目标 URI, 一组断言和一组过滤器定义. 如果断言为真, 路由匹配
- Predicate ( 断言 ) : 输入类型是一个
ServerWebExchange
. 我们可以使用它来匹配来自 HTTP 请求的任何内容, 例如 headers 或 参数. - Filter ( 过滤器 ) : Gateway 中的 Filter 分为两种类型的Filter, 分别是 Gateway Filter 和 Global Filter. 过滤器Filter 将会对请求和响应进行修改处理 .
客户端发请求给服务端。中间有网关。先交给映射器,如果能处理就交给handler 处理,然后交给一系列filer,然后给指定的服务,再返回回来给客户端。
4.4.2、创建模块 gulimall-gateway
并配置API网关
第一步、创建模块 并调整版本一致
创建完项目进行springboot降级 . 并加入到主项目
第二步、引入依赖
<!--SpringCloud-nacos 注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!--SpringCloud-nacos 配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
第三步、开启 服务注册发现
/*
* 1.开启服务注册发现
* */
@EnableDiscoveryClient
@SpringBootApplication
public class GuilmallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GuilmallGatewayApplication.class, args);
}
}
第四步、配置nacos注册中心地址
applicaion.properties
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=gulimall-gateway
server.port=88
第五步、在 nacos 中为
gateway
模块创建名称空间 . 并创建配置
spring:
application:
name: gulimall-gateway
第六步、创建
bootstrap.properties
并填写配置中心地址
spring.application.name=gulimall-gateway
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=064c2e8d-15a5-499a-83c1-1691ccb740d3
4.4.3、测试
测试:
- 模拟输入 http://localhost:88/?url=biadu # 跳到百度页面
- 模拟输入 http://localhost:88/?url=biadu # 跳到qq页面
- 官网教程 :
-
在项目里创建application.yml , 并配置
spring: cloud: gateway: routes: - id: query_route uri: https://www.baidu.com/ predicates: - Query=url,baidu - id: test_route uri: https://www.qq.com predicates: - Query=url,qq
run 起来, 成功啦!
git clone -b 2.0 https://github.com/QingdaoU/OnlineJudgeDeploy.git && cd OnlineJudgeDeploy
docker-compose up -d
docker ps -a
五、前端基础
前后端技术栈类对比
5.1、ES6
ECMAScript6.0(以下简称ES6,ECMAScript是一种由Ecma国际通过ECMA-262标准化的脚本),是JavaScript语言的下一代标准,2015年6月正式发布,从ES6开始的版本号采用年号,如ES2015,就是ES6。ES2016就是ES7。每年一个标准
ECMAScript是浏览器脚本语言规范,JavaScript则是规范的具体实现。
5.1.1、let声明变量
-
var
声明的变量往往会越域,let
声明的变量有严格局部作用域 -
var
可以声明多次,let
只能声明一次 -
var
会变量提升,let
不存在变量提升
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1.var 声明的变量往往会越域
// let 声明的变量有严格局部作用域
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
// 2.var 可以声明多次
// let 只能声明一次
var m = 1;
var m = 2;
let n = 3;
let n = 4;
console.log(m); // 2
console.log(n); // ReferenceError: 'n' is not defined
// 3.var 会变量提升
// let 不存在变量提升
console.log(x); // undefined
var x = 10;
console.log(y); // Cannot access 'y' before initialization
let y = 20;
</script>
</body>
</html>
5.1.2、const声明常量 (只读变量)
- 声明之后不允许改变
- 一但声明必须初始化, 否则会报错
// const 声明之后不允许改变. 一但声明必须初始化, 否则会报错
const a = 1;
a = 3; // const.html:13 Uncaught TypeError: Assignment to constant variable.
5.1.3、解构表达式
5.1.3.1、数组结构
let arr = [1,2,3];
// 以前我们想获取数组中的值, 只能通过角标
let a = arr[0];
let b = arr[1];
let c = arr[2];
// ES6 可以这样, x,y,z将与arr中的每个位置对应来取值
let [x,y,z] = arr;
console.log(a,b,c);
console.log(x,y,z);
5.1.3.2、对象解构
const person = {
name: "jack",
age: 21,
language: ['java','js','css']
}
// 对象解构 (并且还可以fu zhi)
const{name: abc,age,language} = person;
console.log(abc)
console.log(age)
console.log(language);
5.1.4、字符串扩展
5.1.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("hel")); // true
console.log(str.includes("ly")); // false
5.1.4.2、字符串模版
模版字符串相当于加强版的字符串, 用反引号 `, 除了作为普通字符串, 还可以用来定义多行字符串, 还可以在字符串中加入变量和表达式 .
-
多行字符串
// 1、多行字符串 let ss = `<div> <span>hello, world</span> </div>`; console.log(ss);
-
字符串插入变量和表达式. 变量名写在
${}
中,${}
中可以放入 JavaScript 表达式// 2、字符串插入变量和表达式. 变量名写在 `${}` 中, `${}` 中可以放入 JavaScript 表达式 const person = { name: "hgw", age: 22 } const {name,age} = person; let info = `我是${name}, 今年${age + 10}了`; // 我是hgw, 今年32了 console.log(info);
-
字符串中调用函数
// 3、字符串中调用函数 function fun(){ return "这是一个函数"; } let sss = `哈哈哈哈,${fun()}`; console.log(sss); // 哈哈哈哈,这是一个函数
5.1.5、函数优化
5.1.5.1、函数参数默认值
//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(20));
5.1.5.2、不定参数
不定参数用来表示不确定参数个数, 形如,…变量名, 由…加上一个具名参数标识符组成. 具名参数 只能放在参数列表的最后, 并且 有且只有一个不定参数
// 2. 不定参数
function fun(...values){
console.log(values.length);
}
fun(1,2); // 2
fun(1,2,3,4,5); //5
5.1.5.3、箭头函数
ES6 中定义函数的简写方式 :
-
一个参数时
// 3、箭头函数 //以前声明一个方法 // var print = function (obj) { // console.log(obj); // } // 可以简写为: let print = obj => console.log(obj); print("hello");
-
多个参数时
// 多个参数时 // var sum = function (a, b) { // c = a + b; // return a + c; // } let sum2 = (a,b) => a + b; console.log(sum2(1,2)); var sum3 = (a, b) => { c = a + b; return a + c; } console.log(sum3(10, 20)) //------------------------------------------------------------------------------ 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);
5.1.6、对象优化
5.1.6.1、新增的 API
ES6 给 Object 扩展了许多新的方法, 如 :
-
keys(obj)
: 获取对象的所有 key 形成的数组 -
values(obj)
: 获取对象的所有 value 形成的数组 -
entries(obj)
: 获取对象的所有key 和 value 形成的二维数组. 格式:[[k1,v1],[k2,v2],......]
const person = { name: "jack", age: 21, language: ['java','js','css'] } console.log(Object.keys(person)); console.log(Object.values(person)); console.log(Object.entries(person));
-
assign(dest, ...src)
: 将多个src对象的值 拷贝到 dest 中. (第一层为深拷贝, 第二层为浅拷贝)// 声明3个对象 const target = { a:1 }; const source1 = { b:2 }; const source2 = { c:3 }; // 把source1、source2中的合在 target中 Object.assign(target,source1,source2); console.log(target); // {a: 1, b: 2, c: 3}
5.1.6.2、声明对象简写
// 声明对象简写
const age = 23
const name = "张三"
// 传统写法
const person1 = {age: age, name: name};
// 如果属性名 和 属性值变量名一样, 可以如下简写
const person2 = {age,name}; //声明对象简写
console.log(person2);
5.1.6.3、对象的函数属性简写
// 对象的函数属性简写
let person3 = {
name: "jack",
// 以前:
eat: function (food) {
console.log(this.name + "在吃" + food);
},
//箭头函数this不能使用,对象.属性
eat2: food => console.log(person3.name + "在吃" + food),
eat3(food) {
console.log(this.name + "在吃" + food);
}
}
person3.eat("炸鸡");
person3.eat2("米线");
person3.eat3("鸡蛋灌饼");
5.1.6.4、对象扩展运算符
扩展运算符(…) 用于取出参数对象所有可遍历属性然后拷贝到当前对象
// 对象扩展运算符
// 1. 拷贝对象
let personT1 = {name: "Amy", age: 21};
let someone = {...personT1};
console.log(someone); // {name: 'Amy', age: 21}
// 2. 合并对象
let ageT = {age: 15};
let nameT = {name:"Amy"};
let personT2 = {name: "张三"}
personT2 = {...ageT, ...nameT}; // 如果两个对象的字段名重复, 后面对象字段值会覆盖前面对象的字段值
console.log(personT2); // {age: 15, name: 'Amy'}
5.1.7、map 和 reduce
数组中新增了 map 和 reduce 方法.
5.1.7.1、map
Map() : 接收一个函数, 将原数组中的所有元素用这个函数处理后放入新数组返回 .
// 1.Map() : 接收一个函数, 将原数组中的所有元素用这个函数处理后放入新数组返回 .
let arr = ['1','20','-5','3'];
console.log(arr);
arr = arr.map(item => item*2);
console.log(arr);
5.1.7.2、reduce
语法 :
数组名.reduce(callback,[initialValue])
Reduce 为数组中的每一个元素依次执行回调函数, 不包括数组中被删除或从未被赋值的元素, 接收四个参数: 初始值(或者上一次回调函数的返回值), 当前元素值, 当前索引, 调用 reduce 的数组.
callback (执行数组中每个值的函数, 包含四个参数)
previousValue
(上一次调用回调返回的值,或者是提供的初始值(initialValue))currentValue
(数组中当前被处理的元素)index
(当前元素在数组中的索引)array
(调用 reduce 的数组)
// 1.Map() : 接收一个函数, 将原数组中的所有元素用这个函数处理后放入新数组返回 .
let arr = ['1','20','-5','3'];
console.log(arr);
arr = arr.map(item => item*2);
console.log(arr);
// 2.reduce() 为数组中的每一个元素依次执行回调函数, 不包括数组中被删除或从未被赋值的元素
// reduce(callback,[initialValue])
// 1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
// 2、currentValue (数组中当前被处理的元素)
// 3、index (当前元素在数组中的索引)
// 4、array (调用 reduce 的数组)
let result = arr.reduce((a,b)=>{
console.log("上一次处理后:"+a);
console.log("当前正在处理:"+b);
return a + b;
},100);
console.log(result)
5.1.8、promise
以前嵌套ajax的时候很繁琐。
解决方案:
- 把Ajax封装到Promise中,赋值给let p
- 在Ajax中成功使用resolve(data),交给then处理,
- 失败使用reject(err),交给catch处理p.then().catch()
在 JavaScript 的世界中, 所有代码都是单线程执行的. 由于这个“缺陷”, 导致 JavaScript 的所有网络操作、浏览器事件, 都必须是异步执行. 异步执行可以用回调函数实现. 一旦有一连串的 ajax 请求 a,b,c,d… 后面的请求依赖前面的请求结果, 就需要层层嵌套. 这种缩进和层层嵌套的方式, 非常容易造成上下文代码混乱, 我们不得不非常小心翼翼处理内层函数与外层函数的数据, 一旦内层函数使用了上层函数的变量, 这种混乱程度就会加剧… 总之, 这种层叠上下文
的层层嵌套方式, 着实增加了精神的紧张程度 .
案例 : 用户登陆, 并展示该用户的各科成绩. 在页面发送两次请求 :
- 查询用户, 查询成功说明可以登陆
- 查询用户成功, 查询科目
- 根据科目的查询结果, 获取成绩
分析: 此时后台应该提供三个接口, 一个提供用户查询接口; 一个提供科目的接口; 一个提供各科成绩的接口, 为了渲染方便, 最好响应 json 数据. 在这里就不编写后台接口了,而是提供三个 json 文件, 直接提供json数据, 模拟后台接口
corse_score_10.json 得分
{
"id": 100,
"score": 90
}
user.json 用户
{
"id": 1,
"name": "zhangsan",
"password": "123456"
}
user_corse_1.json 课程
{
"id": 10,
"name": "chinese"
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<script>
//1、查出当前用户信息
//2、按照当前用户的id查出他的课程
//3、按照当前课程id查出分数
// $.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);
// }
// });
//1、Promise可以封装异步操作
// let p = new Promise((resolve, reject) => { //传入成功解析,失败拒绝
// //1、异步操作
// $.ajax({
// url: "mock/user.json",
// success: function (data) {
// console.log("查询用户成功:", data)
// resolve(data);
// },
// error: function (err) {
// reject(err);
// }
// });
// });
// p.then((obj) => { //成功以后做什么
// return new Promise((resolve, reject) => {
// $.ajax({
// url: `mock/user_corse_${obj.id}.json`,
// success: function (data) {
// console.log("查询用户课程成功:", data)
// resolve(data);
// },
// error: function (err) {
// reject(err)
// }
// });
// })
// }).then((data) => { //成功以后干什么
// console.log("上一步的结果", data)
// $.ajax({
// url: `mock/corse_score_${data.id}.json`,
// success: function (data) {
// console.log("查询课程得分成功:", data)
// },
// error: function (err) {
// }
// });
// })
function get(url, data) { //自己定义一个方法整合一下
return new Promise((resolve, reject) => {
$.ajax({
url: url,
data: data,
success: function (data) {
resolve(data);
},
error: function (err) {
reject(err)
}
})
});
}
get("mock/user.json")
.then((data) => {
console.log("用户查询成功~~~:", data)
return get(`mock/user_corse_${data.id}.json`);
})
.then((data) => {
console.log("课程查询成功~~~:", data)
return get(`mock/corse_score_${data.id}.json`);
})
.then((data)=>{
console.log("课程成绩查询成功~~~:", data)
})
.catch((err)=>{ //失败的话catch
console.log("出现异常",err)
});
</script>
</body>
</html>
5.1.9、模块化
5.1.9.1、什么是模块化
模块化就是把代码进行拆分, 方便重复利用. 类似java中的导包: 要使用一个包, 必须先导包. 而JS中没有包的概念, 换来的是 模块 .
模块功能主要由两个命令构成: export
和 import
.
export
命令用于规定模块的对外接口 .import
命令用于导入求他模块提供的功能
-
比如我定义一个 js 文件:
hello.js
,里面有一个对象export const util = { sum(a,b) { return a + b; } } // export {util} // ·export` 不仅可以导出对象, 一切JS变量都可以导出. 比如: 基本类型变量、函数、数组、对象.
user.js
,里面有多个变量、方法var name = "jack" var age = 21 function add(a,b){ return a + b; } export {name,age,add}
-
在
main.js
中导入以上两个模块并使用import util from "./hello" import {name,age,add} from "./user" util.sum(1,2); console.log(name); add(1,3);
5.2、Vue
MVVM思想
- M:model 包括数据和一些基本操作
- V:view 视图,页面渲染结果
- VM:View-model,模型与视图间的双向操作(无需开发人员干涉)
视图和数据通过VM绑定起来,model里有变化会自动地通过Directives填写到视view中,视图表单中添加了内容也会自动地通过DOM Listeners保存到模型中 .
操作流程如下:
1、创建一个html程序
2、引入vue.js
3、创建一个vue实例, 关联页面的模版 (将自己的数据渲染到关联的模版,响应式的).
5.2.1、安装
-
下载js并用
<script>
标签引入:<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
-
或者在VScode控制台使用npm install vue导入。步骤分为:
- 先
npm init -y
初始化项目, 生成了一个package.json
文件,说明他是一个npm管理的项目- 类似于maven的pom.xml
npm install vue
,安装后在项目node_modules
里有vue- 类似maven install拉取远程到本地
- 先
5.2.2、声明式渲染 和 双向绑定
绑定元素 : el: '#vue'
:绑定元素的ID
双向绑定 : v-model
当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。
-
data:{name:'伟哥'}
:数据对象中有一个名为name的属性,并设置了初始值 伟哥 -
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
{{msg}}
<h1> {{name}}, 非常帅 , 有{{num}}个人为他点赞</h1>
Mustache 标签将会被替代为对应数据对象上
msg
property 的值。无论何时,绑定的数据对象上msg
property 发生了改变,插值处的内容都会更新。-
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<span v-once>这个将不会改变: {{ msg }}</span>
-
基本案例 :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app" v-model="num">
<input type="text" v-model="num">
<button v-on:click="num++">点赞</button>
<button v-on:click="cancle">取消点赞</button>
<h1> {{name}}, 非常帅 , 有{{num}}个人为他点赞</h1>
</div>
<script>
// 1、vue声明式渲染
// 2、双向绑定, 模型变化、视图变化. 反之亦然
// 3、事件处理
let vm = new Vue({ //生成vue对象
el: "#app", // 绑定元素,div id="app" // 可以指定恰标签,但是不可以指定body标签
data: { // 封装数据
name: "伟哥", // 也可以使用{} //表单中可以取出
num: 1
},
methods: { // 封装方法
cancle(){
this.num --;
}
},
});
// 1. 创建vue实例,关联页面的模版, 将自己的数据(data)渲染到关联的模版, 响应式的
// 2. 指令来简化对dom的一些操作
// 3. 声明方法来做更复杂的操作. methods里面可以封装方法.
</script>
</body>
</html>
5.2.3、基础指令
5.2.3.1、v-text、v-html、v-ref
这两个可以使用data数据。而<div>123{{}}</div>
这种写法叫插值表达式,可以计算,可以取值,可以调用函数
这里还介绍v-html v-text区别
注意取的大多数不是请求域了,而是vue对象里的data
插值闪烁:
使用{undefined{}}方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的
{{}}
,
加载完毕后才显示正确数据,我们称为插值闪烁。
我们将网速调慢一些,然后刷新页面,试试看刚才的案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
{{msg}} {{1+1}} {{hello()}} 前面的内容如果网速慢的话会先显示括号,然后才替换成数据。
v-html 和v-text能解决这个问题
<br/>
用v-html取内容
<span v-html="msg"></span>
<br/>
原样显示
<span v-text="msg"></span>
</div>
<script>
new Vue({
el:"#app",
data:{
msg:"<h1>Hello</h1>",
link:"http://www.baidu.com"
},
methods:{
hello(){
return "World"
}
}
})
</script>
</html>
5.2.3.2、单向绑定 v-bind
问题:花括号只能写在标签体内(<div 标签内> 标签体 </div>
),不能用在标签内。
插值表达式只能用在标签体里,如果我们这么用 <a href="{{}}">
是不起作用的,所以需要 跳转这种用法
解决:用 v-bind:
,简写为 :
。表示把model绑定到view。可以设置src、title、class等
在浏览器里
vm.link="www.baidu.com"
,此处vue数据改了,dom里跳转链接也改了class里有哪些内容可以通过vue数据的bool值添加删除,而在style中代表的是k:v值。
也可以把v-bind:简写成:
{undefined{}}必须有返回值
- 完整语法 :
<a v-bind:href="url">...</a>
- 缩写 :
<a :href="url">...</a>
- 动态参数的缩写 (2.6.0+) :
<a :[key]="url"> ... </a>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
<!-- 给html标签的属性绑定 -->
<div id="app">
<a v-bind:href="link">跳转</a>
<!-- class,style {class名:vue值}-->
<span v-bind:class="{active:isActive,'text-danger':hasError}"
:style="{color: color1,fontSize: size}">你好</span>
</div>
<script>
let vm = new Vue({
el:"#app",
data:{
link: "http://www.baidu.com",
isActive:true,
hasError:true,
color1:'red',
size:'36px'
}
})
</script>
</body>
</html>
你看到的v-bind等被称为指令。指令带有前缀v以表示它们是Vue提供的特殊特性。可能你已经猜到了, 它们会在渲染的DOM上应用特殊的响应式行为在这里,该指令的意思是:“将这个元素节点的title特性和Vue实例的message属性保持一致”。
如果你再次打开浏览器的JavaScript控制台, 输入app, message=‘新消息’,就会再一次看到这个绑定了title特性的HTML已经进行了更新。
5.2.3.3、双向绑定 : v-model
v-bind只能从model到view。v-model能从view到model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- 表单项,自定义组件 -->
<div id="app">
精通的语言:如果是多选框,那么会把每个value值赋值给vue数据
<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="Python"> Python<br/>
选中了 {{language.join(",")}}
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el:"#app",
data:{
language: []
}
})
</script>
</body>
</html>
5.2.3.4、v-on
5.2.3.4.1、基本用法
事件监听可以使用 v-on 指令
v-on:事件类型="方法"
,可以简写成@事件类型="方法"
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
它们看起来可能与普通的 HTML 略有不同,但 :
与 @
对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们
5.2.3.4.2、事件修饰符
事件冒泡:大小div都有单机事件,点了内部div相当于外部div也点击到了。
如果不想点击内部div冒泡到外部div,可以使用.
prevent
阻止事件冒泡用法是 :
v-on:事件类型.事件修饰符="方法"
还可以绑定按键修饰符
v-on:keyup.up=“num+=2” @keyup.down=“num-=2” @click.ctrl=“num=10”
按键修饰符, Vue.js 为 v-on
提供了事件修饰符 .修饰符是由 点开头的指令后缀 来表示的.
.stop
: 阻止事件冒泡到父元素.prevent
: 阻止默认事件发生.capture
: 使用事件捕获模式.self
: 只有元素自身触发事件才执行 . (冒泡或捕获的都不执行).once
: 只执行一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 事件中直接写js片段 -->
<button v-on:click="num++">点赞</button>
<!-- 事件指定一个回调函数,必须是Vue实例中定义的函数 -->
<button @click="cancle">取消</button>
<!-- “Mustache”语法 -->
<button>有{{num}}个赞 </button>
<!-- 事件修饰符 -->
<!-- 让大div只执行一次 -->
<div style="border: 1px solid red;padding: 20px;" @click.once="hello">
大div
<div style="border: 1px solid blue;padding: 20px;" @click.stop="hello">
小div <br/>
<!-- 实现点击不跳转页面并且不执行冒泡行为 -->
<a href="https://www.baidu.com" @click.prevent.stop="hello">去百度</a>
</div>
</div>
<!-- 按键修饰符 -->
<!-- 这里和num双向绑定, 俺上键num+2, 下键num-2 -->
<input type="text" v-model="num" v-on:keyup.up="num+=2" @keyup.down="num-=2" @click.ctrl="num=10"><br/>
提示 :
</div>
<script>
new Vue({
el:"#app",
data:{
num: 1
},
methods: {
cancle() {
this.num--;
},
hello() {
alert("点击了!")
}
},
})
</script>
</body>
</html>
5.2.3.5、v-for遍历
可以遍历 数组[] 字典{} 。对于字典
<li v-for="(value, key, index) in object">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(user,index) in users" :key="user.name">
<!-- 1、显示user信息:v-for="item in items" -->
当前索引:{{index}} ,姓名: {{user.name}}, 性别: {{user.gender}}, 年龄: {{user.age}}
<!-- 2、获取数组下标:v-for="(item,index) in items" -->
<!-- 3、遍历对象:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
-->
<br>
对象信息(v):
<span v-for="v in user">
{{v}},
</span>
<br>
对象信息(v,k):
<span v-for="(v,k) in user">
{{k}}:{{v}},
</span>
对象信息(v,k,i):
<span v-for="(v,k,i) in user">
{{k}} [{{i}}] :{{v}}:,
</span>
</li>
</ul>
<!-- 4、遍历的时候都加上:key来区分不同数据,提高vue渲染效率 -->
<ul->
<li v-for="(num,index) in nums" :key="index"></li>
</ul->
</div>
<script>
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 }
],
nums: [1,2,3,4,4]
},
})
</script>
</body>
</html>
5.2.3.6、v-if和v-show
在vue实例的data指定一个bool变量,然后v-show赋值即可。
show里的字符串也可以比较
if是根据表达式的真假,切换元素的显示和隐藏(操作dom元素)
区别:show的标签F12一直都在,if的标签会移除,
if操作dom树对性能消耗大
v-if和v-show.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!--
v-if,顾名思义,条件判断。当得到结果为true时,所在的元素才会被渲染。
v-show,当得到结果为true时,所在的元素才会被显示。
-->
<div id="app">
<button v-on:click="show = !show">点我呀</button>
<!-- 1、使用v-if显示 -->
<h1 v-if="show">if=看到我....</h1>
<!-- 2、使用v-show显示 -->
<h1 v-show="show">show=看到我</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
</body>
</html>
5.2.3.7、v-else和v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!--
v-if,顾名思义,条件判断。当得到结果为true时,所在的元素才会被渲染。
v-show,当得到结果为true时,所在的元素才会被显示。
-->
<div id="app">
<button v-on:click="show = !show">点我呀</button>
<!-- 1、使用v-if显示 -->
<h1 v-if="show">if=看到我....</h1>
<!-- 2、使用v-show显示 -->
<h1 v-show="show">show=看到我</h1>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
</body>
</html>
5.2.4、计算属性 和 侦听器
5.2.4.1、计算属性computed
什么是计算属性:属性不是具体值,而是通过一个函数计算出来的,随时变化
<body>
<div id="app">
<!-- 某些结果是基于之前数据实时计算出来的, 我们可以利用计算属性来完成 -->
购物车
<ul>
<li>iphone13, 价格: {{ipPrice}}, 数量: <input type="number" v-model="ipNum"></li>
<li>小米9, 价格: {{xmPrice}}, 数量: <input type="number" v-model="xmNum"></li>
<li>总价: {{totalPrice}}</li>
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
ipPrice: 5799,
xmPrice: 3599,
ipNum: 1,
xmNum: 1
},
computed: {
totalPrice() {
return this.ipPrice*this.ipNum + this.xmPrice*this.xmNum
}
},
})
</script>
</body>
5.2.4.2、监听$watch
监听属性 watch,我们可以通过 watch 来响应数据的变化。
以下实例通过使用 watch 实现计数器:
<body>
<div id="app">
<!-- 某些结果是基于之前数据实时计算出来的, 我们可以利用计算属性来完成 -->
购物车
<ul>
<li>iphone13, 价格: {{ipPrice}}, 数量: <input type="number" v-model="ipNum"></li>
<li>小米9, 价格: {{xmPrice}}, 数量: <input type="number" v-model="xmNum"></li>
<li>总价: {{totalPrice}}</li>
{{msg}}
</ul>
</div>
<script>
new Vue({
el: "#app",
data: {
ipPrice: 5799,
xmPrice: 3599,
ipNum: 1,
xmNum: 1,
msg: ""
},
computed: {
totalPrice() {
return this.ipPrice*this.ipNum + this.xmPrice*this.xmNum
}
},
watch: {
ipNum(newVal,oldVal) {
if(newVal>3) {
this.msg = "库存限制不足"
this.ipNum = 3
} else{
this.msg = ""
}
}
},
})
</script>
</body>
5.2.4.3、过滤器filter
定义filter组件后,管道符后面跟具体过滤器
{{user.gender | gFilter}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--第一步:导入vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!-- 过滤器常用来处理文本格式化的操作。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 -->
<div id="app">
<ul>
<li v-for="user in userList">
局部过滤器: {{user.gender | genderFilter}}
全局过滤器: {{user.gender | gFilter}}
</li>
</ul>
</div>
<script>
// 全局过滤器
Vue.filter("gFilter", function(val){
if(val == 1) {
return "男";
} else {
return "女";
}
});
let vm = new Vue({
el: "#app",
data: {
userList: [
{id:1, name:'jacky', gender: 1},
{id:2, name:'peter', gender: 0}
]
},
filters: { // 局部过滤器, 只可以在当前vue实例中使用
genderFilter(val) {
if(val == 1) {
return "男";
} else {
return "女";
}
}
}
})
</script>
</body>
</html>
5.2.5、组件化
vue组件就是自定义标签。组件就是一个模板!
组件是可复用的
Vue
实例, 说白了就是一组可以重复使用的模板, 跟JSTL
的自定义标签、Thymeleal
的th:fragment
等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
5.2.5.1、全局组件
全局注册的组件可以用在其被注册之后的任何 (通过
new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
这里还应该注解的是data那个属性,在组件中需要以函数的方式写
到目前为止,我们只用过 Vue.component
来创建组件:
Vue.component('my-component-name', {
// ... 选项 ...
})
这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue
) 的模板中。比如:
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。
实例 :
<body>
<div id="app">
<counter></counter>
</div>
<script>
// 2、它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
new Vue({
el: "#app"
})
// 1、全局声明注册一个组件
Vue.component("counter",{
template: `<button @click="count++">我被点击了 {{count}} 次(全局)</button>`,
data() {
return {
count: 1
}
}
});
</script>
</body>
5.2.5.2、局部组件
注意:局部注册的组件在其子组件中不可用
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
- 在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
- 然后在
components
选项中定义你想要使用的组件:
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
对于 components
对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。
实例:
<body>
<div id="app">
<button-counter></button-counter>
</div>
<script>
// 2、局部声明一个组件
const buttonCounter = {
template: `<button @click="count++">我被点击了 {{count}} 次(局部)</button>`,
data() {
return {
count: 1
}
}
};
new Vue({
el: "#app",
components: {
'button-counter': buttonCounter
}
})
</script>
</body>
5.2.6、生命周期钩子函数
每个vue实例在被创建时都要经过一系列的初始化过程:创建实例,装载模板、渲染模板等等。vue为生命周期中的每个状态都设置了钩子函数(监听函)。每当vue实列处于不同的生命周期时,对应的函数就会被触发调用。
5.2.7、Axios异步通信
咱们开发的接口大部分都是采用JSON格式, 可以先在项目里模拟一段JSON数据, 数据内容如下:创建一个名为data.json的文件并填入上面的内容, 放在项目的根目录下
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--v-clock:解决闪烁问题-->
<style>
[v-clock]{
display: none;
}
</style>
</head>
<body>
<div id="vue" v-clock>
<div>{{info.name}}</div>
<div>{{info.address.street}}</div>
<a v-bind:href="info.url">点我刷新</a>
</div>
<!--第一步:导入vue.js axios.js的包-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm = new Vue({
el: '#vue',
//data:属性:vm固有的
data(){
return{
// 请求的返回参数格式,必须和 json字符串一样
info:{
name: null,
url: null,
address: {
street: null,
city: null,
country: null
},
}
}
},
mounted(){ //钩子函数 链式编程 ES6新特性
//get一个请求得到一个结果,then response得到一个结果,=> 指向得到一个结果要处理的事情
axios.get('../data.json').then(response=>(this.info=response.data));
}
});
</script>
</body>
</html>
说明:
-
在这里使用了v-bind将a:href的属性值与Vue实例中的数据进行绑定
-
使用axios框架的get方法请求AJAX并自动将数据封装进了Vue实例的数据对象中
get一个请求得到一个结果,then response得到一个结果,=> 指向得到一个结果要处理的事情
-
我们在data中的数据结构必须和
Ajax
响应回来的数据格式匹配!
5.2.8、vue模块化开发
5.2.8.1、安装工具
(这个是工具,与具体项目无关)
# 安装webpack
npm install webpack -g
# 安装vue脚手架
npm install -g @vue/cli-init
5.2.8.2、初始化 vue 项目
在项目文件夹里执行
本人在这里创建了一个 文件夹vue-demo
# 初始化vue项目
vue init webpack vue-demo
# 运行,访问8080端口
npm run dev
5.2.8.3、vue项目目录结构:
目录/文件 | 说明 |
---|---|
build | 项目构建(webpack)相关代码 |
config | 配置目录,包括端口号等。我们初学可以使用默认的。 |
node_modules | npm 加载的项目依赖模块 |
src | src 这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件: - assets: 放置一些图片,如logo等。 - components: 目录里面放了一个组件文件,可以不用。 - App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。 - main.js: 项目的核心文件。<br/ |
static | 静态资源目录,如图片、字体等。 |
test | 初始测试目录,可删除 |
.xxxx文件 | 这些是一些配置文件,包括语法配置,git配置等。 |
index.html | 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。 |
package.json | 项目配置文件。 |
README.md | 项目的说明文档,markdown 格式 |
cmd属性,去掉快速编辑
1.index.html
index.html只有一个<div id="app">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue-demo</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
2.main.js
main.js中import,并且new Vue
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在 Vue 构造器中有一个el 参数,它是 DOM 元素中的 id。 这意味着我们接下来的改动全部在以上指定的 div 内,div 外部不受影响。
3.router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Hello from '@/components/Hello'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
4.App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<router-link to="/hello">去hello</router-link>
<router-link to="/">去首页</router-link>
<!-- 路由视图 -->
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- 其中的路由视图标签是根据url要决定访问的vue
- 在main.js中提及了是使用的./router
5.src/components/Hello.vue
new Vue中指定了元素,元素如何渲染看template,组件决定元素样式
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li>
<a
href="https://vuejs.org"
target="_blank"
>
Core Docs
</a>
</li>
<li>
<a
href="https://forum.vuejs.org"
target="_blank"
>
Forum
</a>
</li>
<li>
<a
href="https://chat.vuejs.org"
target="_blank"
>
Community Chat
</a>
</li>
<li>
<a
href="https://twitter.com/vuejs"
target="_blank"
>
Twitter
</a>
</li>
<br>
<li>
<a
href="http://vuejs-templates.github.io/webpack/"
target="_blank"
>
Docs for This Template
</a>
</li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li>
<a
href="http://router.vuejs.org/"
target="_blank"
>
vue-router
</a>
</li>
<li>
<a
href="http://vuex.vuejs.org/"
target="_blank"
>
vuex
</a>
</li>
<li>
<a
href="http://vue-loader.vuejs.org/"
target="_blank"
>
vue-loader
</a>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
>
awesome-vue
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App (hgw)'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
6、config/index.js
vue项目开放端口太麻烦了,其他主机访问不了。防火墙输入策略和host什么的修改过,nacos等其他服务能正常访问,vue项目别的主机访问不了,不知道哪里有限制
可以在config/index.js配置vue项目的ip和端口
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
5.3、element-ui
使用Vue 整合一下 element-ui 官网教程
5.3.1、安装
# 直接npm安装,在项目中执行
npm i element-ui -S
# 或者引入样式
5.3.2、使用
在 main.js 中写入以下内容:
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
// 让vue使用ElementUI组件
Vue.use(ElementUI);
然后.vue文件中写标签, 比如此处在Hello.vue中加入了两个单选框
<template>
<div>
<h1>你好,Hello {{name}}</h1>
<el-radio v-model="radio" label="1">点赞</el-radio>
<el-radio el-radio v-model="radio" label="2">取消点赞</el-radio>
</div>
</template>
<script>
export default {
data() {
return {
name: "张三",
radio: "2"
}
}
}
</script>
<style >
</style>
更多推荐
所有评论(0)