请求流程先在网页上请求然后传到nginx再到eureka容器再到zuul再到微服务:
leyou-registry是eureka的module
leyou-geteway是zuul网关的module
leyou-item是一个聚合模块,<packaging>pom</packaging>是声明打包方式为pom,声明是聚合模块
leyou-item里面有一个leyou-item-interface,它的作用是相当于存放一些pojo对象的,也存放一些异常,iangge工程调用都可以用这些异常放在goods,也存放其他的工程需要的东西
leyou-item里面有一个leyou-item-service是微服务模块,对商品的管理增删改查都是在这个模块中进行的
leyou-common是存放工具类
每个包具体功能如下:
如下图 ,goods是商品,当商品服务工程需要处理goods就要用goods对象,当商品管理工程处理
goods也要弄一个对象,这样就会发生当一个goods发生变化而另外的工程的goods还没有反应过来,可以将goods做成一个依赖,这样就两个工程都可以用它。
如下图的是扫描的pojo对象,但是就要有这个路径让他扫描,所以以后到这一步就要想起来有没有这个路径,而且这个路径是在interface中,但是这个application.yml是在leyou-item-service中的,不过leyou-item-service中引入了leyou-item-interface依赖,所以即使application.yml是在leyou-item-service中的也是可以扫描application.yml是在leyou-item-interface中的路径
注意:npm install vue@2.5.16 --save其中@2.5.16就是项目需要什么版本的版本号就下载这个的版本号
后台前端已经搭建完毕:
build:是webpack的一些文件。
config:webpack运行的一些环境参数,例如运行完以后的端口为9001就是在在config中的index.js改的。
diat:打包的一个目录,比如vue就有dist
static:静态资源
package:比较重要,就相当于pom.xlm,添加依赖和配置
src:assets是存放资源的,图片,scc啥的
components:全局定义我们的组件
pages:是页面本质也是组件,pages下的layou.vue下的<template>存放模板,<script>存放vue实例的js代码。后台管理的前台是一个单页面应用,如图:
这里面只有一个app容器,它的实例在src下的main.js中
项目后台前端的运行流程
Vuetify框架,基于vue的UI框架
其中在controller的“Responseentity,这个的作用就是将tostring的字符数据转化为json的数据格式,我认为这是它的最大作用。
跨域问题:
解决问题:本项目采用的是cors的反向代理
什么是cors
简单请求是八种
特殊请求
在网关里添加一个config包,里面有一个LeyouCorsConfiguration类就是配置的跨域请求,别的里面也能陪但是为什么配到网关里?因为每个服务都要访问这个网关的,所以都可以用,如果只配置到单一的服务里,以后别的服务在访问就又要配置了。
请求处理过程红色线是处理数据请求,黑色线是处理显示页面过程:
比如一个http://api.leyou.com/api/item/category/list?pid=0的地址,先去host看是不是本地地址然后去nginx中根据设置的server_name:api.leyou.com proxy_pass:http://127.0.0.1:10010在后边根据10010监听到找到10010以后还剩api/item/category/list?pid=0,然后去zuul网关里面找到prefix为api的routers为中根据网址的api的后面的网址找到相对应的routers,例子是item所以就去找微服务名字为item-service的微服务,进去名字为item-service的微服务以后网址中路径还剩category/list?pid=0,进入Controller中找到Mapper为category的然后再去找list下边的方法将pid参数传进去然后再原路返回
响应数据添加@ResponseEntity,接收数据加@ResquesdBody
如果前台传过的数据是json数据再Controller里面只能有一个对象为参数,这时候可以用一个第三方工具qs工具,可以用命令npm install qs --save将json转换成一个一个的字符串
页面的校验
如上图红色的提示就是在BrandFrom.vue文件的required :rules:"nameRules"就是规定必填还有填入的规则
图片上传: 图片上传绕过网关:再nginx中修改然后直接绕过zuul网关到微服务
因为文件上传的都是有upload的路径,所以在配置文件中判如果是这个路径的就路由到8082其他的都到10010中
在nginx中location越大的越给后边放
fastDFS:是分布式文件系统->
规格参数:SPU SKU
sku才是一个商品的细分,比如手机金色还是黑色每一个颜色都是一个sku
spu包含一推东西,比如手机是小米八怎么换颜色换内存都是小米八,是一个总的集合就是spu
如下图中有个
是否为通用字段回传给数据库的 tb_spec_param然后有个是否通用,前端传过去的是flase或者true,但是数据库是0或1,这是因为generic的数据类型tinyint,然后如果在前台显示的话先判断是不是通用的字段。如果是flase那就去tb_sku里的own_spec找相关的内容,如果是ture那就去tb_spu_detail里面generic_spec找内容
数据库表的关系
表结构场景解析:渲染前端的商品详情页的参数:
先点击商品获取spu_id
那个数字就是sku表的spu_id可以查询到spu表的信息,spu表中有个三级分类cid1,cid2,cid3.根据三级的分类可以定位到组也就是tb_spec_group表所对应的cid字段渲染到页面如下图
可以渲染成这样
然后如果想看
标记这一点是tb_spec_group里面通过cid去tb_spec_param找对应的cid然后渲染出来
想要有详情介绍就是字段就是在上边说的有spu_id然后去spu_detial表中拿到相关的数据包括generic_spec的json字段。
如何渲染下边这个页面
根据spu_id去spuDetial找到special_spec字段的值渲染到上边再获取special_spec渲染多少GB等等
产品参数规格的设置:后台设置:SpecificationController和SpecificationService是同时处理SpecGroupMapper和SpecParamMapper以及这两个的pojo实体类。
注意:在开发过程中发现一个通用Mapper的一点注意点(没学过Mapper),例如查询代码:
CollectionUtils是来处理集合的,基本是用来判断集合是否为空集合的长度如下图:
如果不加 注释掉的那一行specParam.setGroupId(gid);那么就是查询的全部的,因为return里面用的通用Mapper的select个人实验是:因为select括号里是一个对象specParam(实体类),所以呢这个对象很对字段,它是根据字段查询的,比如现在加了注释赋值的地方,所以这个实体类的字段都是空的,所以查询的时候都是全部显示出来,如果有一个字段有值的话那么就按照这个值来查询,如果有两个的话就多条件查询,显示同时满足这里这两个字段的。
在处理新增的数据的时候,在controller层中添加@RequestBody注解来响应;
再给后台的商品管理里面的规格参数里里做增删改查,里面的接口名称一样但是为什么访问的不一样呢,是因为里面的而访问请求不一样如下图:
一个是post一个是put,所以前端的请求post的去后端的postMapping,请求put的去putMapping。
修改我用的是通用mapper的updateByPrimaryKey如下图:
package com.leyou.item.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.mapper.BrandMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Spu;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Lym
* @version 1.0
* @date 2021/11/12 17:30
*/
@Service
public class GoodsServise {
@Autowired
private SpuMapper spuMapper;
@Autowired
private SpuDetailMapper spuDetailMapper;
@Autowired
private BrandMapper brandMapper;
@Autowired
private CategoryService categoryService;
public PageResult<SpuBo> querSpuByPage(String key, Boolean saleable, Integer page, Integer rows) {
Example example = new Example(Spu.class);
Example.Criteria criteria = example.createCriteria();
//添加条件查询
if (StringUtils.isNotBlank(key)) {
criteria.andLike("title", "%" + key + "%");
}
//添加上下架的过滤条件
if (saleable != null) {
criteria.andEqualTo("saleable", saleable);
}
//添加分页
PageHelper.startPage(page, rows);
//执行查询,获取spu集合
List<Spu> spus = this.spuMapper.selectByExample(example);
PageInfo<Spu> pageInfo = new PageInfo<>(spus);
//spu集合转化成spubo集合
List<SpuBo> spuBos = spus.stream().map(spu -> {
SpuBo spuBo = new SpuBo();
BeanUtils.copyProperties(spu, spuBo);
//查询品牌名称
Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
spuBo.setBname(brand.getName());
//查询分类名称
List<String> names = this.categoryService.queryNameById(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
spuBo.setCname(StringUtils.join(names, "-"));
return spuBo;
}).collect(Collectors.toList());
//返回PageResult<SpuBo>
return new PageResult<>(pageInfo.getTotal(),spuBos);
}
}
如上图,这是一个想要展示一个页面所需的几个步骤,这只是例子,具体想要怎么弄看情况
如上图两个空是下拉选择框,第一个之前做了一个分类,但是第二个空是再第一个选择完以后根据他的三级目录的id来查询,所以要做一个新的功能,因为通用mapper没有这个查询方法,所以写了个sql语句
@Select("SELECT * FROM tb_brand a INNER JOIN tb_category_brand b ON a.id=b.brand_id WHERE b.category_id=#{cid}")
List<Brand> selectBrandsByCid(Long cid);
这是内连接,为什么要用内连接,是因为他的品牌名称在tb_brand的表中,而前边三级目录选择下拉是在tb_—category—brand表中,所以需要两张表链接,为什么不left jion?因为如果左连接,这样的话如果穿的是空的id也会出现品牌,这样就会出现第一个下拉框没有传进去id第二个下拉框就会出现品牌,右链接是传进去id可能会出现空的品牌,这样下拉框下拉选择的时候会出现空的品牌选择这样都是不合理的。
如上两个图的实现功能代码如下
public List<SpecParam> queryParams(Long gid,Long cid,Boolean generic,Boolean searching) {
SpecParam specParam = new SpecParam();
specParam.setGroupId(gid);
specParam.setCid(cid);
specParam.setSearching(searching);
specParam.setGeneric(generic);
return this.specParamMapper.select(specParam);
}
是实现在tb_spec_param这张表,这张表就是手机的参数。一共四个参数,可以选择如果gid是空就查cid以此类推。