金山编译器出问题了。下面段落标号全出问题了,排版也出问题了。懒得改了。
使用对象存储OSS,保存品牌logo
-
新建Module,提供上传、显示服务
-
有些不明所以的,按照steinliving-commodity配置了一通pom.xml
-
新建application.yml,配置需要使用的6个参数
-
前端组件替换原有logo组件,提供上传功能
-
解决跨域
-
从客户端到微服务的跨域。在后端使用config.CorsConfig的配置即可
-
从客户端到OSS端的跨域。
-
关于url。
-
后端代码的host="https://"是这样开头的。
-
但是前端的组件申请访问时,用的url是"http://",如果用https是会出错,无法访问的。
-
上传正常,但是页面显示的只是网址,如何直接显示图片。使用插槽机制实现。
<template slot-scope="scope">
<img :src="scope.row.logo" style="width: 80px">
</template>
项目架构
1.提出问题
1)目前我们的项目创建了多个独立微服务模块,但还不是分布式的
2)带来的问题主要是各个微服务模式独立,不能统一的进行管理和调度
2. 解决问题,重构项目
nacos注册发现中心
-
下载并解压,通过运行startup.cmd启动Nacos
-
通过浏览器访问localhost:8848/nacos/
-
在公共模块common添加nacos的<denpendency>,这样子模块都不必再次引用了
<!--公共模块的引用,可以被子模块们间接引用,因此不必在子模块重复写-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--只控制版本,不负责引入-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<!--下面的pom和import组合使用,实现了多继承-->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
-
在comodity配置nacos的配置-application.yml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置nacos的服务地址
application:
name: steinliving-commodity
-
在主启动类加入注解,启动Nacos
@EnableDiscoveryClient
重启主启动类commodity:9090,完成刷新。重新登录nacos的网页,便可以在服务列表,查看到commodity的服务,完成注册发现。
编辑
service7070配置nacos
因为service也引入common模块,所以只需要直接在application.yml配置即可。同上第4步。
主启动类添加启动nacos。同上第5步。
启动,登录页面,完成测试。同上第6步。
编辑
renren-fast注册到nacos
因为renren-fast没有引入nacos,也没有引入common模块,所以需要先单独引入nacos(老韩是引入的common,如果后期报错,则改过来)
然后配置->@Enable ->启动 ->测试。同上4-6。
gateway网关
将gateway也注册到nacos
新建steinliving-gateway微服务模块
pom.xml的引用
不能使用web引用,因为gateway不是web服务,会报错
需要common引用,因为用它间接引入nacos,实现注册
另外要排除mybatis-plus,否则会有DateSource之类的报错,无法启动
添加gateway的引用,完成它的功能
可以保留test的引用,但我没有引入,后期需要再补上。
使用dependencyManagement来进行gateway的版本控制
<dependencies>
<dependency>
<groupId>com.stein.stein-living</groupId>
<artifactId>steinliving-common</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--如何确定版本?通过版本控制选择的某一个版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<!--因为spring-cloud-gateway不是spring-boot-alibaba的组件,所以这儿的dependency也不一样-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<!--同样设置成多继承-->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
nacos的常规配置。application.yml
端口
提供服务的ip+端口
应用名称
server:
port: 5050
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: steinliving-gateway
创建启动类并添加注解@Enable...等。运行,到nacos页面查看是否已经显示服务。
编辑
使用gateway完成服务转发
利用application.yml来配置gateway的转发规则
routes 设置转发规则
- id:一个符号都不能少,注意中间的空格。
uri:需要和id对齐,而不是和“-”减号对齐
predicates: 断言,进行路径匹配,成功后,和上面的uri进行组合。url=uri+Path
- Path= 注意大小写,然后后面是用的等号“=”,而不是冒号“:”。可以使用通配符
可以设置多组,后面的以此照此继续配置。
server:
port: 5050 #保持不变
spring:
cloud:
gateway:
routes: #参数顺序不要乱改。试过,运行不起
- id: test_route01 # 自定义,需唯一
uri: http://localhost:9090
predicates: #断言:按Path进行匹配,匹配成功,则转发到uri所在的服务上
- Path=/commodity/brand/list
- id: test_route02 #再来一个测试,转发到百度新闻
uri: http://news.cctv.com/ #笑scren,百度国际没有了,只好改成了cctv
predicates:
- Path=/world/index.shtml
配置转发renren-fast
注意这个服务名是可以在uri里面使用的,还不要写错了
编辑
配置application.yml
spring:
cloud:
gateway:
routes: #参数顺序不要乱改。试过,运行不起
- id: steinliving-renren-fast #自定义一个renren-fast的id
uri: lb://renren-fast #lb表示支持负载均衡。会动态读取nacos的ip+port,实现负载均衡。
predicates:
- Path=/api/** #注意这儿的/api只是起断言匹配的作用,在url中会去掉,叫做"路径重写"
filters: #通过过滤器来达到"路径重写"的目的。
- RewritePath=/api/(?<segment>.*), /renren-fast/$\{segment}
# 这个()括号写成了{ },排查半天
# segment是变量名,可以自定义,前后须一致
# ","逗号前是改写前的,(?<segment>.*)得到后面uri片段
# ","逗号后面的是改写后的,加上前面得到的片段,进行拼接
# 当然这儿不是必须的,在Path=/renren-fast/即可。目的是为了学习一种新方法。
# 语法与正则表达式有关。
写完上面的内容后,还需要改写前端的内容,需要把请求发送到gateway/api,才能生效。
// 找到api接口请求地址
window.SITE_CONFIG['baseUrl'] = 'http://localhost:8080/renren-fast'; 改写为:
window.SITE_CONFIG['baseUrl'] = 'http://localhost:5050/api';
解决“跨域”问题
gateway的跨域
由于gateway没有引用Web包,所以之前的解决办法没有用。这样解决:
@ConfigurationpublicclassGatewayCorsConfiguration {
@Beanpublic CorsWebFilter corsWebFilter(){
System.out.println("enter....");
UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource();
CorsConfigurationcorsConfiguration=newCorsConfiguration();
//1、配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
returnnewCorsWebFilter(source);
}
}
renren-fast的跨域
解决了gateway的跨域后,还是不能登录,提示:
不允许有多个'Access-Control-Allow-Origin'CoRS头
所以还要注解掉renren-fast之前解决跨越的配置类。然后正常访问。
配置转发commodity服务
-
依然在gateway配置application.yml的routes
- id: steinliving-commodity #自定义一个renren-fast的id
uri: lb://steinliving-commodity # 从nacos的列表中复制,避免写错
predicates:
- Path=/api/commodity/** #这儿是精准匹配,要放在/api/**这条的前面,否则出错401
filters: #通过过滤器来达到"路径重写"的目的。
- RewritePath=/api(?<segment>.*), $\{segment} #只是取消了/api这部分,多体会写法
-
修改前端转发的url
-
增 addCategory(data)
url: this.$http.adornUrl('/commodity/category/save'),
-
删 remove(node, data)
url: this.$http.adornUrl('/commodity/category/delete'),
-
改 update(data)
url: this.$http.adornUrl('/commodity/category/update'),
-
查 getCategories()
url: this.$http.adornUrl('/commodity/category/list/tree'),
-
多头跨域
前面修改后,发现无法登录,依然是'Access-Control-Allow-Origin'CoRS头的问题。注解掉commodity的跨域配置类,只保留gateway的。
-
小坑
修改了url,注解了commodity的配置类,依然显示跨越问题,无法登录。
打开maven,找到commodity,clean->package,然后便正常了
配置转发service
同上...
配置
service的前端url在src/components/upload/policy.js修改
brand品牌管理的界面内容还要修改src/views/modules/commodity/brand.vue里的uri
解决跨域
Nacos作为配置中心
以oss-service为例,其余同理。
创建命名空间
总共3种方法,这儿使用命名空间配置。
在命令空间创建namespace。
pom.xml引入nacos-config
因为nacos-discovery已经在common里面导入了,这儿就不再重复引入了。
<!--引入nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<!--<version>2.1.0.RELEASE</version>-->
</dependency>
版本控制
如果不加pom和import,会导致maven报错,无法引入。这儿是多继承的意思。
<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. 新建配置
在“配置列表”选择对应的命名空间,然后新建配置
DateID:需要有文件的后缀名。例如.yml,否则不能启动
Group: dev表示开发,pro表示产品上线,test表示测试
配置格式:YAML
内容:把原来yml中的配置部分,拷贝到这儿即可。原有内容可以注解掉,最好不要删。
5.在IDEA中重写一个配置。
这儿用了一种另外的方法,提高眼界
新建bootstrap.properties
12行的.dataId可能是改版了。之前的写的是data-id。这儿就按提示的写的,也正常运行了。
# 注册到nacos的name
spring.application.name=steinliving-service
# 指定nacos服务注册和发现地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 指定nacos配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 指定获取配置信息的namesapce
spring.cloud.nacos.config.namespace=dbbeb6b5-c3a3-475a-a255-4a6871277690
# 指定namespace下的组
spring.cloud.nacos.config.group=dev
# 通过eXt-config.[?]来指定data-id和是否实时刷新获取的配置
spring.cloud.nacos.config.ext-config[0].dataId=steinliving-oss-service-config.yml
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[0].group=dev
前后端校验:单独分出去了一篇
后面当随笔写了。给自己看的笔记。
新增分组显示功能
1. mysql中新建表格
2. ren-ren fast快速生成代码
3. 测试代码是否正常
4. 创建菜单
5. 完善前端页面功能
页面的显示是跟“菜单Url”绑定的。之前不显示的原因竟然是,需要重启下前端dev。
完善查询和分页功能
查询功能
需要自己定义查询哪些列,以及是否使用模糊查询等
前端
点击获取当前属性,@node-click=“事件方法”
注意:获取数据池中的数据,都需要使用this.xxx来获取。
分页功能
使用分页插件
在config/下新建一个配置类,类名自定义,比如MyBatisConfig
@Configuration
@EnableTransactionManagement //启用事务管理
@MapperScan("com.stein.steinliving.commodity.dao") //扫描哪儿?dao层
public class CustomPageConfig {
// 引入分页插件
@Bean // 注意这个别忘了
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 是否设置超过总页数,就显示第一页
paginationInterceptor.setOverflow(true);
// 设置单页最大显示条数
paginationInterceptor.setLimit(100);
return paginationInterceptor;
}
}
使用Element-UI的Cascader级联选择器,完成添加属性组功能
1. 得到数据
通过getCategory()方法返回数据
2. 引入级联选择器
通过element-ui引入:
删除不用的 @change="handleChange";
添加props属性;
<el-cascader
v-model="categoryCascaderIds"
:options="categories"
:props="props"
></el-cascader>
3. 绑定到级联选择器
props属性,列出对应字段的值来自于option的哪个属性
v-model=“categoryCascaderIds”,而value:"id",说明cascader的层级数据是来自于各层级的"id"
lable,显示标签,各层级显示的内容,通过哪个字段获得
children,子级是哪个字段
expandTrigger,为什么没有效果?。。。
props: {
value: "id",
label: "name",
children: "childrenCategories",
expandTrigger: 'hover'
},
存在问题:
1. 会一直显示到空子级,也就是多了一级空的。
解决办法:使用@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonInclude注解
是jackSon中最常用的注解之一,是为实体类在接口序列化返回值时增加规则的注解
例如,一个接口需要过滤掉返回值为null的字段,即值为null的字段不返回
NON_EMPTY排除字段值为null、空字符串、空集合、空数组、Optional类型引用为空,AtomicReference类型引用为空,即值为empty的字段不返回。
2. 新增id显示为空
解决办法:
a.前端。将该字段的默认值由“”(空字符串)改为0
b.后端。将提交的数据表单的属性值中,从层级数据中取出最后一个放到categoryId
3. 级联选项的回显不正确。解决时踩的坑:
a.在后端把该功能写好了后,先用postman进行测试,如果正常,再整合到前端。避免出错。
b. 注意id字段跟categoryId字段不要混淆了
c. 集合list应该放在方法里面新建。每调用一次方法,重建一个list,更新一次数据。不能放在属性里面新建,否则list会一直增长,显示异常。
4.在级联显示里面添加“搜索”功能
在el标签里面添加filterable即可。 <el-cascader filterable>
5.解决"新增"按键,残留级联数值
通过新增方法,属性值置为空即可。
实践发现使用,@close绑定事件可以完成清空。而使用@click却不能完成该任务。
原因不明?感觉跟生命周期有关,估计哪儿冲突了,通过关闭时清空避免冲突。
操作分类与品牌关联—设计中间表
它们是多对多的关系。对于大表,会根据需要设计一些冗余字段来提高效率,比如这里的brand_name和category_name。
1.创建mysql数据表。疑问,为什么不使用依赖来检查一致性?
2.仍然使用renren-generator逆向工程生成对应的代码,并拷贝到hspliving-commodity
3.测试是否正常使用
功能“关联分类”
前端添加界面
注意多个参数,使用{ }体包起来
参数赋值的对应关系,避免“安错”位置
后端
三层结构的设计,注意各层对应的逻辑
this关键字的理解
@Service("服务名")的理解
数据回显
点击商品品牌,显示该品牌Id的关联列表
@RequestMapping("/brand/list")
public R categoryBrandListByBrandId(@RequestParam Long brandId) {
List<CategoryBrandRelationEntity> data = categoryBrandRelationService.list(
new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id", brandId));
return R.ok().put("data", data);
}
难点在于查询包装器的使用:
1.使用QueryWrapper包装器
2.这个参数的写法,感觉类似流式处理。查询条件是:“brandId”(数据库的字段名)=brandId(参数)
3.使用<CategoryBrandRelationEntity>来校验返回类型是否正确。
注意
1.这儿的参数Long brandId,因为前端传入的名称,和后端一致,所以这儿省略了注解@RequestParam("brandId")的value值
2.前端参数传递的格式
params: this.$http.adornParams({"brandId":this.brandId},false)
添加业务表格:基础属性
创建mysql表格
设计保存基础属性的表格。
使用renren-fast完成基础的增删改查
完善页面功能:
1. 实现下拉选项菜单。可以使用ElememtUI的选择器实现。
2. 增加cascader层级选择器
3. 实现从“商品”->“商品所在组”->“商品属性”的层级对应。使得选择不同的商品,可以根据商品的特征,自动匹配所属的商品属性。需要添加连接"商品"-"商品属性"的中间表"商品所在组"。
<el-form-item label="商品基本属性">
<el-select ref="abc" v-model="dataForm.baseAttrGroupId" placeholder="请选择">
<el-option
v-for="item in baseAttrGroup"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
注意:v-model后面赋值的属性,不能用this.dataForm.baseAttrGroupId这样的方式。否则,选项出来了,点击却选不上。
那么问题来了:倒底什么时候要用this,什么时候不用?大概是在方法里面才用this。?
完善 商品“基本属性”和“属性-属性组关联"
创建mysql表格
设计保存基础属性的表格。
使用renren-fast完成基础的增删改查
后端:
完善save方法,涉及多表事务操作,需要使用@Transactional注解
销售属性表的增删改查
1.完善属性表“查询”功能
后端
@Override
public PageUtils searchResultPage(Map<String, Object> params, Long categoryId) {
// 先获取出要查询的内容
String key = (String)params.get("key");
// 使用查询包装器进行查询
QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<>();
// 要求查询基本属性
queryWrapper.eq("attr_type",1);
// 输入查询条件
queryWrapper.and(wrapper ->{
wrapper.eq("attr_id",key).or().like("attr_name",key);
});
// category默认为0。有值时,添加筛选条件
if(categoryId != 0){
queryWrapper.eq("categoryId",categoryId);
}
// 将查询回来的结果进行封装,放到分页显示器里面
IPage<AttrEntity> page = this.page(
new Query<AttrEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}
}
2.解决“级联数据”,在页面刷新后,不能正常回显
通过从后端返回数据进行回显。
使用递归算法,算出级联数组,并返回。
前面做过类似的算法,直接引用即可。