《谷粒商城》项目前期准备及前端基础
一、项目简介
1.项目背景
gulimall(谷粒商城) 项目是一套B2C(Business-to-Customer)的电商项目,包括前台商城系统以及后台管理系统,基于 SpringCloud + SpringCloudAlibaba + MP实现,采用 Docker 容器化部署。
- 前台商城系统包括:用户登录、注册、商品搜索、商品详情、购物车、下订单流程、秒杀活动等模块。
- 后台管理系统包括:系统管理、商品系统、优惠营销、库存系统、订单系统、用户系统、内容管理等七大模块。
2.项目架构图
3.项目微服务划分
二、环境搭建和项目准备
1.安装Linux(VM+FinalShell)
2.虚拟机网络设置
3.Linux安装Docker
4.docker安装mysql
- 从docker hub拉取mysql:5.7的镜像进行安装
-
创建实例并启动
docker run -p 3306:3306 #端口映射,左虚拟机右容器,这样通过访问Linux虚拟机的3306端口,就能访问到容器里的3306端口的mysql了 --name mysql \ #起的容器名叫mysql #-v:数据卷挂载,[宿主机]:[容器] -v /mydata/mysql/log:/var/log/mysql \ #将Linux中的/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 #后台运行,以mysql:5.7版本镜像运行
-
修改MySQL配置
[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
修改后restart mysql容器
5.docker安装redis
- 从docker hub拉取redis:5.0.5的镜像进行安装
-
创建容器并启动
mkdir -p /mydata/redis/conf touch /mydata/redis/conf/redis.conf docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf
-
修改配置开启 Redis AOF持久化
appendonly yes
6.统一开发环境
(1)JDK-1.8
(2)maven-3.6.1并配置阿里云镜像
(3)idea安装lombok和mybatisx插件
(4)vscode并安装插件
(5)配置git并设置SSH免密登录
(6)项目结构创建并提交到码云
-
创建微服务:商品服务(product)、仓储服务(ware)、订单服务(order)、优惠券服务(coupon)、用户服务(member)
微服务 端口号 gulimall-coupon 7000 gulimall-member
8000 gulimall-order
9000 gulimall-product
12000 gulimall-ware
11000 -
每个微服务项目的共同部分:
- 技术集:springWeb、openFeign
- 包名:com.zyb.gulimall.xxx
- 模块名:gulimall-xxx
- 将gulimall聚合为父工程:https://blog.nowcoder.net/n/17811d4142e5441b98e84e4c72729b6d
(7)数据库初始化
分别创建上述微服务对应的数据库:gulimall_xms
7.创建后台管理系统
使用码云人人开源项目中的renren-fast和renren-fast-vue来快速搭建后台管理系统及其前端管理功能。
- 将renren-fast和renren-fast-vue克隆到本地
- 将renren-fast聚合到gulimall项目中
-
搭建renren-fast运行环境
- 创建后台管理数据库gulimall_admin
- 修改renren-fast的配置文件
- 运行
- 在VScode中运行renren-fast-vue,并测试
8.项目基本增删改查的生成
使用人人开源项目中的renren-generator来快速生成项目基本的增删改查。这里以gulimall-product项目为例。
- 将renren-generator克隆到本地
- 将renren-generator聚合到gulimall项目中
- 修改配置文件中的数据库信息(想给哪个微服务生成增删改查就连接哪个数据库,这里连接的是gulimall_psm)
- 修改generator.properties文件中的配置信息
- 运行,访问localhost:80
- 这里我们全选,逆向生成所有表格的代码,解压出来是这样的:
- 将上面的main文件夹复制到gulimall-product中的main。
-
可以看到代码有很多报错的地方,这是因为有一些依赖和公共的工具类我们还没导入。我们创建一个gulimall-common项目,将每个微服务的公共依赖和类放在这里面,让每个微服务都去依赖它就行:
- 在gulimall-common的pom中添加公共依赖
-
- 将renren-fast中的公共工具类复制到gulimall-common
-
9.整合MP并测试gulimall-product项目
(1)整合MP
- 导入MP依赖(已在gulimall-common中导入)和mysql驱动坐标mysql-connector-j
-
配置数据库信息
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver //根据mysql版本决定留不留cj url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC username: root password: ****
-
配置全局生成策略控制,让每一个pojo都使用相同的主键生成策略
mybatis-plus: global-config: db-config: id-type: auto
(2)测试
10.分布式组件
(1)SpringCloudAlibaba版本选择
- 这里我的springboot是2.1.8.RELEASE,springcloud是Greenwich.SR3,所以springcloudalibaba选择2.1.0.RELEASE版本。
-
在gulimall-common中导入springcloudalibaba 2.2.7.RELEASE版本依赖:
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.7.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
(2)Nacos注册中心
-
启动nacos服务:Nacos 本身就是一个 SprintBoot 项目,进入bin目录,执行命令直接启动nacos:
./startup.cmd -m standalone # 单机启动
-
在gulimall-common中导入nacos客户端依赖,服务的注册发现(即用来将微服务注册到nacos注册中心,包括发现其他微服务):
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
【tips】因为每个微服务都需要注册到注册中心,即都需要这个公共依赖,所以直接把该依赖放到common中。以后有这种公共依赖都可以放到common中。
-
在各个微服务中配置nacos服务地址和微服务名称:
spring: cloud: nacos: discovery: server-addr: localhost:8848 # nacos服务地址 application: name: gulimall-coupon
-
在启动类上使用@EnableDiscoveryClient注解来开启服务注册与发现功能(把该微服务注册到nacos):
@EnableDiscoveryClient @SpringBootApplication public class GulimallCouponApplication {
(3)Feign声明式远程调用
比如member调用coupon中的方法。
-
导入坐标:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在调用者member的启动类上添加注释开启Feign功能:
@EnableFeignClients()
-
在member中编写Feign客户端接口(用来基于 SpringMVC 的注解 @GetMapping 来声明远程调用的信息):
@FeignClient("gulimall-coupon")//被调用者服务名 public interface CouponFeignClient { //声明调用被调用服务的哪个请求的方法,所以这里既有完整请求路径"/coupon/coupon/member/list",又有对应的方法getMemberCoupons() @GetMapping("/coupon/coupon/member/list") public R getMemberCoupons(); }
-
member调用coupon中的方法:
@RequestMapping("/coupons") public R testFeigin(){ MemberEntity memberEntity = new MemberEntity(); memberEntity.setCity("beijing"); //远程调用coupon服务的getMemberCoupons()方法 R memberCoupons = couponFeignClient.getMemberCoupons(); return R.ok().put("member",memberEntity).put("coupons",memberCoupons.get("coupons")); }
- 调用流程:在浏览器输入localhost:8000/member/member/coupons后,会遇到couponFeignClient.getMemberCoupons()方法,然后去我们编写的couponFeignClient接口中找远程调用的信息:请求路径"/coupon/coupon/member/list"+方法getMemberCoupons(),来远程调用coupon服务。
(4)Nacos配置中心
微服务的所有配置信息都可以在nacos配置中心配置,只需要在bootstrap.yml中说明要加载的配置文件信息即可。本项目使用nacos配置中心的规则:
①每个微服务创建自己的命名空间,使用配置分组来划分环境:dev/test/prod。
②不要将所有配置信息放在一个配置文件中,比如数据源的配置单独抽出来、mysql的、nacos的等都抽取成各自的配置文件,方便管理。
- 在nacos中创建统一配置文件
-
从nacos拉取配置
-
在common中导入nacos-config坐标
<!--nacos配置管理依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
-
创建 bootstrap.yml,配置nacos服务信息(这些配置信息决定了项目启动时去哪个nacos配置中心读取哪个配置文件)
spring: application: name: gulimall-coupon # 服务名称 cloud: nacos: server-addr: localhost:8848 # Nacos地址 config: file-extension: yaml # 文件后缀
-
在common中导入nacos-config坐标
-
动态获取配置
-
在controller上使用@RefreshScope注解开启自动刷新配置(配置修改后不需要重启项目就能获取到)
@RefreshScope //开启配置自动刷新,不需要重启项目 @RestController @RequestMapping("coupon/coupon") public class CouponController {
-
在变量声明上面使用@Value("${配置项}")注解获取配置文件中指定配置项的值
@Value("${coupon.user.name}") private String name; @Value("${coupon.user.age}") private int age;
-
在controller上使用@RefreshScope注解开启自动刷新配置(配置修改后不需要重启项目就能获取到)
-
可以使用命名空间实现配置隔离
- 创建命名空间
- 在微服务中配置要读取哪个命名空间下的配置
-
-
可以使用分组实现同一配置文件的不同应用场景
- 创建同一配置时指定分组
- 在微服务中配置要读取哪个分组下的配置
- ★同时加载多个配置文件:
(5)Gateway网关
网关不光能进行权限控制,还支持路由和负载均衡以及限流控制!(用到再去笔记里看)
三、前端基础快速入门
1.ES6新特性
ES6(ECMAScript 6.0)是浏览器脚本语言的规范(标准),而各种我们熟知的 js 语言,如 JavaScript 则是规范的具体实现。类似与JDBC是规范,mysql各种驱动是具体实现。
(1)let 声明变量
(2)const声明常量(只读变量)
(3)解构表达式
- 数组解构
- 对象解构
(4)字符串扩展
-
ES6 为字符串扩展了几个新的 API:
- includes():返回布尔值,表示是否包含参数字符串
- startsWith():返回布尔值,表示是否以参数字符串开始
- endsWith():返回布尔值,表示是否以参数字符串结尾
-
- 字符串模板:模板字符串相当于加强版的字符串,用反引号 `。除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式
(5)函数优化
- 函数参数默认值
- 不定参数:不定参数用来表示不确定个数参数,形如...变量名,由...加上一个具名参数标识符组成。 具名参数只能放在参数列表的最后,并且有且只能有一个不定参数
- 箭头函数
(6)对象优化
-
ES6 给 Object 拓展了许多新的方法:
- keys(obj):获取对象的所有 key 形成的数组
- values(obj):获取对象的所有 value 形成的数组
- entries(obj):获取对象的所有 key 和 value 形成的二维数组。格式:[[k1,v1],[k2,v2],...]
- assign(dest, ...src) :将多个 src 对象的值拷贝到 dest 中。(第一层为深拷贝,第二层为浅拷贝)
-
- 声明对象简写
- 对象的函数属性简写
- 对象扩展运算符(...):用于取出参数对象所有可遍历属性然后拷贝到当前对象
(7)数组中新增了 map 和 reduce 方法
- map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
- 语法:arr.reduce(callback,[initialValue])
-
reduce 为数组中的每一个元素(不包括数组中被删除或从未被赋值的元素)依次执行回调函数,接收四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
-
callback(执行数组中每个值的函数,包含四个参数)
- previousValue(上一次调用回调返回的值,或者是提供的初始值initialValue)
- currentValue (数组中当前被处理的元素)
- index (当前元素在数组中的索引)
- array (调用 reduce 的数组)
- initialValue 作为第一次调用 callback 的第一个参数。
-
-
callback(执行数组中每个值的函数,包含四个参数)
(8)promise
JavaScript 中所有代码都是单线程执行的,导致 JavaScript 的所有网络操作、浏览器事件,都必须是异步执行。异步执行可以用回调函数实现。一旦有一连串的 ajax 请求,后面的请求依赖前面的请求结果,就需要层层嵌套。这种缩进和层层嵌套的方式,非常容易造成上下文代码混乱。
案例:用户登录,并展示该用户的各科成绩。在页面发送两次请求:
- 1. 查询用户,查询成功说明可以登录
- 2. 查询用户成功,查询科目
- 3. 根据科目的查询结果,获取成绩
【tips】完成这个案例后台应该提供三个接口(用户查询接口、科目接口、各科成绩接口),为了渲染方便,最好响应 json 数据。在这里就不编写后台接口了,而是提供三个 json 文件,直接提供 json 数据,模拟后台接口:
promise可以解决这种层层嵌套的问题。
-
promise语法
像下面这样,在 promise 中就封装了一段异步执行的结果:
使用箭头函数可以简写为:
-
处理异步结果
如果想要等待异步执行完成,做一些事情,可以通过 promise 的 then 方法来实现。 如果想要处理 promise 异步执行失败的事件,还可以跟上 catch:
-
使用promise改造上述案例
-
优化处理:通常在企业开发中,会把 promise 封装成通用方法,如下:封装了一个通用的 get 请求方法
(9)模块化
-
什么是模块化?
模块化就是把代码进行拆分,方便重复利用,类似 java 中的导包。而 JS 中没有包的概念,与之对应的是模块。
-
模块功能主要由两个命令构成:
-
export命令:用于规定模块的对外接口,即将模块导出去。
①比如定义一个 js 文件hello.js,里面有一个util对象,可以使用 export 将这个对象导出,别的文件就可以用这个对象了:
【tips】export不仅可以导出对象,一切 JS 变量都可以导出。比如:基本类型变量、函数、数组、 对象。
②上面的导出代码中,都明确指定了导出的变量名,别人在导入使用时就必须准确写出变量名,否则就会出错。 因此 js 提供了default关键字,可以对导出的变量名进行省略,这样导入使用时就可以任意起名字了:
-
import命令:用于导入其他模块提供(导出)的功能。
【tips】批量导入要加{}。
-
export命令:用于规定模块的对外接口,即将模块导出去。