通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven+Linux+Docker+Nginx - 《01》

项目效果演示:

[video(video-XmXsRKp2-1745292676448)(type-csdn)(url-https://live.csdn.net/v/embed/474705)(image-https://v-blog.csdnimg.cn/asset/1ab367e031405a8c7b8b5955d576328e/cover/Cover0.jpg)(title-通用型产品发布解决方案演示)]

项目的源码地址:

当你一个项目模块当中的 pom.xml 指明的 parent 父类,不是一个你本地上的配置的 module 。而是远程仓库当中一个 module

当你的一个项目模块当中的 pom.xml 其中指明的 parent 父类,不是一个你本地上配置的一个 module 模块(就是不是一个本地当中可以找到的内容),而是某个远程仓库当中一个 module

比如:Spring-Cloud 就会发生一个报错信息,我们可以配置在对应的配置上加上一个查询顺序的方式。进行处理解决。

    <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
<!--
从父级仓库查找依赖仓库
maven 构建jar 包时候查找顺序: relativePath 元素中的地址 > 本地仓库 > 远程仓库
<relativePath/> 设定一个空值将始终从仓库中获取,不从本地路径获取。
-->
</parent>

跨域问题,解决(设计到一个跨域,没有云存储)

跨域问题:我们的浏览器是默认禁止我们进行一个跨域访问的,所谓的跨域访问就是:当我们一个微服务模块的访问另外一个微服务模块的,这两微服务模块的端口号是不同的,比如 8080 端口——> 访问的是 9090 端口的。这样就存在一个跨域的问题了。

解决跨域问题:

递归层级分类显示

{
"msg": "success",
"code": 0,
"page": {
"totalCount": 0,
"pageSize": 10,
"totalPage": 0,
"currPage": 1,
"list": [
{
"id": 1,
"name": "家用电器",
"parentId": 0,
"catLevel": 1,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 2,
"name": "家居家装",
"parentId": 0,
"catLevel": 1,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 21,
"name": "大 家 电",
"parentId": 1,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 22,
"name": "厨卫大电",
"parentId": 1,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 41,
"name": "家纺",
"parentId": 2,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 42,
"name": "灯具",
"parentId": 2,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 201,
"name": "燃气灶",
"parentId": 22,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 202,
"name": "油烟机",
"parentId": 22,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 301,
"name": "平板电视",
"parentId": 21,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 601,
"name": "桌布/罩件",
"parentId": 41,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 602,
"name": "地毯地垫",
"parentId": 41,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 651,
"name": "台灯",
"parentId": 42,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
},
{
"id": 652,
"name": "节能灯",
"parentId": 42,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": null
}
]
}
}

增加一个数据库当中没有的属性(类)字段的思想

import com.baomidou.mybatisplus.annotation.TableField;

	/**
* 增加一个属性,childrenCategories
* 1.childrenCategories 表示某个分类的子分类集合
* 2.childrenCategories 没有对应表 commodity_category 字段
* 3. @TableField(exist = false) 表示 childrenCategories 不对应表的字段
*/
@TableField(exist = false)
private List<CategoryEntity> childrenCategories;
package com.rainbowsea.rainbowsealiving.commodity.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable;
import java.util.List; import lombok.Data; /**
* 商品分类表
*
* @author rainbowsea
* @email rainbowsea@gmail.com
* @date 2025-03-04 16:38:22
*/
@Data
@TableName("commodity_category")
public class CategoryEntity implements Serializable {
private static final long serialVersionUID = 1L; /**
* id
*/
@TableId
private Long id;
/**
* 名称
*/
private String name;
/**
* 父分类 id
*/
private Long parentId;
/**
* 层级
*/
private Integer catLevel;
/**
* 0 不显示,1 显示]
*/
private Integer isShow;
/**
* 排序
*/
private Integer sort;
/**
* 图标
*/
private String icon;
/**
* 统计单位
*/
private String proUnit;
/**
* 商品数量
*/
private Integer proCount; /**
* 增加一个属性,childrenCategories
* 1.childrenCategories 表示某个分类的子分类集合
* 2.childrenCategories 没有对应表 commodity_category 字段
* 3. @TableField(exist = false) 表示 childrenCategories 不对应表的字段
*/
@TableField(exist = false)
private List<CategoryEntity> childrenCategories; }

是一个递归的方式,增加的一个属性。

增加子分类集合属性

	/**
* 增加一个属性,childrenCategories
* 1.childrenCategories 表示某个分类的子分类集合
* 2.childrenCategories 没有对应表 commodity_category 字段
* 3. @TableField(exist = false) 表示 childrenCategories 不对应表的字段
*/
@TableField(exist = false)
private List<CategoryEntity> childrenCategories;

 /**
* 核心方法: 返回所有分类及其子分类(带有层级关系-即树形)
* 这里我们会使用 java8的,流式计算(stream api) + 递归操作(有一定难度)
*
* @return
*/
@Override
public List<CategoryEntity> listTree() {
// 思路分析-步骤:
// 1. 查出所有的分类数据
List<CategoryEntity> entities = baseMapper.selectList(null); // 2.组装成层级树形结构使用到 Java8 的 stream api + 递归操作
// 思路:
// 1.过滤,返回1级分类
// 2.2 进行 map 映射操作,给每个分类设置对应的子分类(这个过程会使用到递归)
// 2.3 进行排序 sorted 操作
// 2.4 将处理好的数据收集/转换到集合 // 3.返回 带有层级关系数据-即树形
// 需求:从 List 中过滤出 person.id % 2 != 0 的 person对象
// list.stream() : 把 List 转成流对象,目的是为了使用流的方法,
// 这样就可以处理一些比较负载的业务 List<CategoryEntity> categoryTree =
entities.stream().filter(categoryEntity -> {
// 2.1 过滤filter,返回 1级分类
return categoryEntity.getParentId() == 0; // 0 就是一级分类
}).map(category -> {
// 2.2 进行map映射操作,给每个分类设置对应的子分类(这个过程会使用到递归)
category.setChildrenCategories(getChildrenCategories(category,entities));
return category;
}).sorted((category1, category2) -> {
// 2.3 进行排序sorted 操作,按照 sort 的升序排列
return (category1.getSort() == null ? 0 : category1.getSort()) -
(category2.getSort() == null ? 0 : category2.getSort());
}).collect(Collectors.toList()); // // 2.4 将处理好的数据收集 collect/转换到集合中 // 3. 返回带有层级关系的-即树形
return categoryTree;
} /**
* 递归查询所有的分类的子分类
* * 该方法的任务就是把 root 下的所有子分类的层级关系组织好,并返回。
* * all 就是所有的分类信息(即上个方法的 entities )
*
* @param root
* @param all
* @return
*/
private List<CategoryEntity> getChildrenCategories(CategoryEntity root,
List<CategoryEntity> all) {
// 1.过滤
List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
return categoryEntity.getParentId() == root.getId();
}).map(categoryEntity -> {
// 2. 找到子分类,并设置递归
categoryEntity.setChildrenCategories(getChildrenCategories(categoryEntity, all));
return categoryEntity;
}).sorted((category1, category2) -> {
// 按照 sort 排序-升序
return (category1.getSort() == null ? 0 : category1.getSort()) -
(category2.getSort() == null ? 0 : category2.getSort());
}).collect(Collectors.toList()); // 将处理好的数据收集 collect/转换到集合中
return children;
} }

{
"msg": "success",
"code": 0,
"data": [
{
"id": 1,
"name": "家用电器",
"parentId": 0,
"catLevel": 1,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 21,
"name": "大 家 电",
"parentId": 1,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 301,
"name": "平板电视",
"parentId": 21,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
}
]
},
{
"id": 22,
"name": "厨卫大电",
"parentId": 1,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 201,
"name": "燃气灶",
"parentId": 22,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
},
{
"id": 202,
"name": "油烟机",
"parentId": 22,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
}
]
}
]
},
{
"id": 2,
"name": "家居家装",
"parentId": 0,
"catLevel": 1,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 41,
"name": "家纺",
"parentId": 2,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 601,
"name": "桌布/罩件",
"parentId": 41,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
},
{
"id": 602,
"name": "地毯地垫",
"parentId": 41,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
}
]
},
{
"id": 42,
"name": "灯具",
"parentId": 2,
"catLevel": 2,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": [
{
"id": 651,
"name": "台灯",
"parentId": 42,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
},
{
"id": 652,
"name": "节能灯",
"parentId": 42,
"catLevel": 3,
"isShow": 1,
"sort": 0,
"icon": "",
"proUnit": "",
"proCount": 0,
"childrenCategories": []
}
]
}
]
}
]
}

前端层级展示部分

MyBatis-plus 逻辑删除

查询的时候,会添加上,我们的在 application.yaml 当中配置的逻辑条件的(自动的方式)。

一句话: 表指定一个字段,标识是否显示该记录,而不是真正删除该记录

逻辑删除支持

mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
import com.baomidou.mybatisplus.annotation.TableLogic;

public class User {
// 其他字段... @TableLogic
private Integer deleted;
}

例子:

spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.100:3306/hspliving_commodity
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver # 配置 mybatis-pus
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto # 配置主键信息
logic-delete-value: 0 # 逻辑已经被删除值(默认为1,这里我们调整为我们自己的 0 )
logic-not-delete-value: 1 # 逻辑未被删除值(默认值为0,这里我们调整成我们自己的)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

MySQL 前端添加内容,中文乱码,mysql5.7 以后

useSSL=false 和 true 的区别

useSSL=false和true的区别_usessl=false与true的区别-CSDN博客

useSSL=false和true的区别:

SSL(Secure Sockets Layer 安全套接字协议),在mysql进行连接的时候,如果mysql的版本是5.7之后的版本必须要加上useSSL=false,mysql5.7以及之前的版本则不用进行添加useSSL=false,会默认为false,一般情况下都是使用useSSL=false,尤其是在将项目部署到linux上时,一定要使用useSSL=false!!!,useSSL=true是进行安全验证,一般通过证书或者令牌什么的,useSSL=false就是通过账号密码进行连接,通常使用useSSL=false!!!

spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.100:3306/hspliving_commodity?useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver

Vue 报错 取消 ESLint 检测

renren-generator 自动生成 crud 代码

renren-generator 默认是 80 端口,浏览器默认就是 80 端口,不需要特别指明该 80 端口。

  • renren-generator 是一个代码生成器,可生成 entity ,xml,dao,service,vue,sql 代码,减少 70% 的开发认为。
  • renren-generator 模块的官方地址:https://gitee.com/renrenio/renren-generator

  1. 删除 renren-generator 项目的 .git 目录,并拷贝到 RainbowSealiving 项目下,当做一个子模块 (如图)

  1. 修改 RainbowSealiving/pom.xml , 将 renren-generator 加入到父项目,统一管理

<!--    指定聚合管理的模块/微服务模块-->
<modules>
<module>RainbowSealiving-commodity</module>
<module>renren-fast</module>
<module>renren-generator</module>
<module>RainbowSealiving-common</module>
<module>raibnowsealiving-service</module>
<module>rainbowsealiving-gateway</module>
</modules>
  1. 修改 RainbowSealiving\renren-generator\src\main\resources\application.yml , 配置数据库,修改为我们自己的数据库的配置信息

server:
port: 80 # mysql
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#MySQL配置
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.56.100:3306/hspliving_commodity?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
#oracle配置
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@47.100.206.162:1521:xe
# username: renren
# password: 123456
#SQLServer配置
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://192.168.10.10:1433;DatabaseName=renren_fast
# username: sa
# password: 123456
#PostgreSQL配置
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://192.168.10.10:5432/renren_fast
# username: postgres
# password: 123456 jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
resources:
static-locations: classpath:/static/,classpath:/views/ #mongodb:
# host: localhost
# port: 27017
# auth: false #是否使用密码验证
# username: tincery
# password: renren
# source: 123456
# database: test mybatis-plus:
mapperLocations: classpath:mapper/**/*.xml pagehelper:
reasonable: true
supportMethodsArguments: true
params: count=countSql #指定数据库,可选值有【mysql、oracle、sqlserver、postgresql、mongodb】
renren:
database: mysql
  1. 修改 RainbowSealiving\renren-generator\src\main\resources\generator.properties , 指定生成代码的相关信息

#\u4EE3\u7801\u751F\u6210\u5668\uFF0C\u914D\u7F6E\u4FE1\u606F
mainPath=com.rainbowsea
#\u5305\u540D
package=com.rainbowsea.rainbowsealiving
moduleName=commodity
#\u4F5C\u8005
author=rainbowsea
#Email
email=rainbowsea@gmail.com
#\u8868\u524D\u7F00(\u7C7B\u540D\u4E0D\u4F1A\u5305\u542B\u8868\u524D\u7F00)
# 数据表的前缀
tablePrefix=commodity_

完整的 generator.properties 的配置信息

#\u4EE3\u7801\u751F\u6210\u5668\uFF0C\u914D\u7F6E\u4FE1\u606F
mainPath=com.rainbowsea
#\u5305\u540D
package=com.rainbowsea.rainbowsealiving
moduleName=commodity
#\u4F5C\u8005
author=rainbowsea
#Email
email=rainbowsea@gmail.com
#\u8868\u524D\u7F00(\u7C7B\u540D\u4E0D\u4F1A\u5305\u542B\u8868\u524D\u7F00)
# 数据表的前缀
tablePrefix=commodity_
#\u7C7B\u578B\u8F6C\u6362\uFF0C\u914D\u7F6E\u4FE1\u606F
tinyint=Integer
smallint=Integer
mediumint=Integer
int=Integer
integer=Integer
bigint=Long
float=Float
double=Double
decimal=BigDecimal
bit=Boolean
char=String
varchar=String
tinytext=String
text=String
mediumtext=String
longtext=String
date=Date
datetime=Date
timestamp=Date
NUMBER=Integer
INT=Integer
INTEGER=Integer
BINARY_INTEGER=Integer
LONG=String
FLOAT=Float
BINARY_FLOAT=Float
DOUBLE=Double
BINARY_DOUBLE=Double
DECIMAL=BigDecimal
CHAR=String
VARCHAR=String
VARCHAR2=String
NVARCHAR=String
NVARCHAR2=String
CLOB=String
BLOB=String
DATE=Date
DATETIME=Date
TIMESTAMP=Date
TIMESTAMP(6)=Date
int8=Long
int4=Integer
int2=Integer
numeric=BigDecimal
nvarchar=String
  1. 修改 RainbowSealiving\renren-generator\pom.xml ,替换掉 自动生成的 parent 配置, 这个配置信息可以从 rainbowseaiving-commodity 模块拷贝即可

 <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath></relativePath>
</parent>
  1. 启动RainbowSeaiving\renrengenerator\src\main\java\io\renren\RenrenApplication.java 保证没有错误

  1. 访问浏览器的 localhost:80

注意:我们这里的覆盖是将 main 覆盖的时候,已有的文件并不会被覆盖,没有的数据会被添加上去。

注意:我们需要查看 renren-generator 自动生成的 CRUD 的代码,它所生成的包路径是否,满足你的要求,或者是有错误需要处理。

阿里云存储-oss 配置

用户登录名称 rainbowsealiving-10000@1562236733667461.onaliyun.com
AccessKey ID LTAI5tP4G6hDJqh7FPe1Cahh
AccessKey Secret vl5kaBORH1QADEzKq9NInpRdD8JJeF
LTAI5tP4G6hDJqh7FPe1Cahh
vl5kaBORH1QADEzKq9NInpRdD8JJeF
用户登录名称 rainbowsealiving-10000@1562236733667461.onaliyun.com
AccessKey ID LTAI5tP4G6hDJqh7FPe1Cahh
AccessKey Secret vl5kaBORH1QADEzKq9NInpRdD8JJeF

编写一个简单的上传文件:

import java.io.*;
import java.util.Random;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.OSSObjectSummary; public class OssJavaSdkQuickStart {
/** 生成一个唯一的 Bucket 名称 */
public static String generateUniqueBucketName(String prefix) {
// 获取当前时间戳
String timestamp = String.valueOf(System.currentTimeMillis());
// 生成一个 0 到 9999 之间的随机数
Random random = new Random();
int randomNum = random.nextInt(10000); // 生成一个 0 到 9999 之间的随机数
// 连接以形成一个唯一的 Bucket 名称
return prefix + "-" + timestamp + "-" + randomNum;
} public static void main(String[] args) throws com.aliyuncs.exceptions.ClientException {
// 设置 OSS Endpoint 和 Bucket 名称
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
String bucketName = generateUniqueBucketName("demo");
// 替换为您的 Bucket 区域
String region = "cn-hangzhou";
// 创建 OSSClient 实例
EnvironmentVariableCredentialsProvider credentialsProvider =
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.region(region)
.build();
try {
// 1. 创建存储空间(Bucket)
ossClient.createBucket(bucketName);
System.out.println("1. Bucket " + bucketName + " 创建成功。");
// 2. 上传文件
String objectName = "exampledir/exampleobject.txt";
String content = "Hello OSS";
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
System.out.println("2. 文件 " + objectName + " 上传成功。");
// 3. 下载文件
OSSObject ossObject = ossClient.getObject(bucketName, objectName);
InputStream contentStream = ossObject.getObjectContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(contentStream));
String line;
System.out.println("3. 下载的文件内容:");
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
contentStream.close();
// 4. 列出文件
System.out.println("4. 列出 Bucket 中的文件:");
ObjectListing objectListing = ossClient.listObjects(bucketName);
for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
System.out.println(" - " + objectSummary.getKey() + " (大小 = " + objectSummary.getSize() + ")");
}
// 5. 删除文件
ossClient.deleteObject(bucketName, objectName);
System.out.println("5. 文件 " + objectName + " 删除成功。");
// 6. 删除存储空间(Bucket)
ossClient.deleteBucket(bucketName);
System.out.println("6. Bucket " + bucketName + " 删除成功。");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException | IOException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}

spring cloud 整合

使用 SpringCloud Alibaba OSS 传文件到阿里云对象 Bucket

<!--        引入 oss starter 我们引入的 stater 和 文档给的不一样,请注意观察 artifactId-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

修改 RainbowSealiving\RainbowSealiving-commodity\src\main\resources\application.yml , 增加 oos 相关

配置参数

spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.100:3306/hspliving_commodity?useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver
# 配置 阿里云 oss
cloud:
alicloud:
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com # 杭州位置
access-key: LTAI5tP4G6hDJqh7FPe1Cahh
secret-key: vl5kaBORH1QADEzKq9NInpRdD8JJeF
# 配置 mybatis-pus
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto # 配置主键信息
logic-delete-value: 0 # 逻辑已经被删除值(默认为1,这里我们调整为我们自己的 0 )
logic-not-delete-value: 1 # 逻辑未被删除值(默认值为0,这里我们调整成我们自己的)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

    // 装配 OSSClient
@Resource
private OSSClient ossClient; // 上传指定的文件到 bucket 当中
@RequestMapping("/test2")
public R testUpload2() throws FileNotFoundException {
InputStream inputStream = new FileInputStream("E:\\Java\\project\\RainbowSealiving\\redis.jpg");
ossClient.putObject("rainbowsealiving-10000","redis.jpg",inputStream);
ossClient.shutdown();
// redis.jpg
return R.ok("上传 OK");
}

第三方接入(微服务模块)

配置 pom.xml 文件信息

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <groupId>com.rainbowsea.RainbowSealiving</groupId>
<artifactId>raibnowsealiving-service</artifactId>
<version>1.0-SNAPSHOT</version>
<name>raibnowsealiving-service</name>
<description>顺平( 家居生活)- 第三方服务模块 OSS</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.5</spring-cloud.version>
</properties> <dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.1.0.RELEASE</version>
</dependency> <!-- 引入公共模块-->
<dependency>
<groupId>com.rainbowsea.RainbowSealiving</groupId>
<artifactId>RainbowSealiving-common</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 排除公共模块当中的 mybatis-plus-boot-starter 依赖-->
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

生成一个时间戳的文件格式,用于存储/备份信息


//指定过期时间,单位为秒。
Long expire_time = 3600L; /**
* 通过指定有效的时长(秒)生成过期时间。
* @param seconds 有效时长(秒)。
* @return ISO8601 时间字符串,如:"2014-12-01T12:00:00.000Z"。
*/
public static String generateExpiration(long seconds) {
// 获取当前时间戳(以秒为单位)
long now = Instant.now().getEpochSecond();
// 计算过期时间的时间戳
long expirationTime = now + seconds;
// 将时间戳转换为Instant对象,并格式化为ISO8601格式
Instant instant = Instant.ofEpochSecond(expirationTime);
// 定义时区
ZoneId zone = ZoneId.systemDefault(); // 使用系统默认时区
// 将 Instant 转换为 ZonedDateTime
ZonedDateTime zonedDateTime = instant.atZone(zone);
// 定义日期时间格式,例如2023-12-03T13:00:00.000Z
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
// 格式化日期时间
String formattedDate = zonedDateTime.format(formatter);
// 输出结果
return formattedDate;
}

跨域处理-CORS 问题(设计到 2 个跨域,有云存储)

两个位置,

  1. 一个是我们商品模块 9090 访问图片服务模块的 7070 端口的一个,不是同源策略上的跨域问题:

添加上:

/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/ package com.rainbowsea.rainbowsealiving.service.config; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration
public class CorsConfig implements WebMvcConfigurer { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}
  1. 第二个则是我们,我们的图片服务 7070 访问阿里云(或者其他的云)的一个,不是同源策略上的 CORS 问题。解决-阿里云上设置。

pom.xml 排除公共模块

<!--        引入公共模块,排除 mybatis-plus -->
<dependency>
<groupId>com.rainbowsea.RainbowSealiving</groupId>
<artifactId>RainbowSealiving-common</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 排除公共模块当中的 mybatis-plus-boot-starter 依赖-->
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>

在公共模块引入 nacos + Gateway,简化配置

目前我们的项目创建了多个独立微服务模块,但还不是分布式的

带来的问题主要是各个微服务模式独立,不能统一的进行管理和调度,

  1. 可以配置上,网关 Nacos 上的自动调度,或者集群
  2. 可以一些 Spring Cloud 微服务的配置信息,放置到 Nacos 上,进行读取,而不是放置到本地。‘

这里我们可以配置,放置在公共模块(微服务)当中,用于其他的微服务模块的访问和使用。


<!--
解读
1. dependencyManagement是包依赖管理
2.该微服务模块的 pom.xml
的<dependencies></dependencies>引入 spring cloud alibaba
相关依赖的时候,就不需要指定版本了
3. 仅仅只是声明,并没有对引入加载相关的依赖信息。
-->
<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>

        <!--
解读
1. 引入 nacos 服务注册/ 发现
2. 以后,引入了 hspliving-common 的微服务模块就有了 nacos服务注册/发现依赖,不需要在引入 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

当我们已经在“公共模块” 的服务当中,引入配置上了,相关的 Nacos ,同时,该对应的服务的 pom.xml 文件当中导入了相关的公共模块。下面就需要我们在对应的服务当中,配置对应 Spring Cloud 微服务的上的 Nacos 配置信息即可。

对应上的 Nacos 配置信息,可以配置到 properties 配置,或者是 yaml 配置文件。都可以,这里使用的是 yaml 配置文件,注意:层级关系,层级对应是在 Spring cloud 下的

spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.100:3306/hspliving_commodity?useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver
# 配置 阿里云 oss
cloud:
alicloud:
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com # 杭州位置
access-key: LTAI5tP4G6hDJqh7FPe1Cahh
secret-key: vl5kaBORH1QADEzKq9NInpRdD8JJeF
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务的地址
application:
name: RainbowSealiving-commodity # 该微服务的 name 信息

同时在对应微服务的启动类上,启动服务注册发现。加入注册发现服务注解

package com.rainbowsea.rainbowsealiving.commodity;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; // dao 路径扫描映射
// @MapperScan指定扫描的dao包,如果我们在Dao指定的有 @Mapper ,也可以不写
//@MapperScan("com.rainbowsea.rainbowsealiving.commodity.dao")
@EnableDiscoveryClient
@SpringBootApplication
public class RainbowSealivingCommodityApplication {
public static void main(String[] args) {
SpringApplication.run(RainbowSealivingCommodityApplication.class, args);
}
}

完成测试: 启动相关配置好的微服务, rainbowsealiving-commodity, 看看是否注册到 Nacos

在来一个,微服务配置上 Nacos 服务。

server:
port: 7070
# 配置 oss
spring:
cloud:
alicloud:
access-key: LTAI5tP4G6hDJqh7FPe1Cahh
secret-key: vl5kaBORH1QADEzKq9NInpRdD8JJeF
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com # 杭州位置
bucket: rainbowsealiving-10000
# 将 raibnowsealiving-service 注册到 nacos 当中去。
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: raibnowsealiving-service

文件覆盖-策略-就上面的一个通过时间戳的方式创建的文件,进行备份,记录的方式

上引。

管理版本仲裁依赖

可以通过, 进行一个版本上的声明,和管理版本。

注意:这种方式,是不会实际引入到相关的依赖的,仅仅只是声明

    <!--
解读
1. dependencyManagement是包依赖管理
2.该微服务模块的 pom.xml
的<dependencies></dependencies>引入 spring cloud alibaba
相关依赖的时候,就不需要指定版本了
3. 注意:这里仅仅只是声明了,并没有进入实际上的 依赖。
-->
<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>
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>RainbowSealiving</artifactId>
<groupId>com.rainbowsea.RainbowSealiving</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>RainbowSealiving-common</artifactId>
<description>公共模块</description> <!-- 引入相关依赖-->
<dependencies> <!--引入mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!-- mybatis-plus 依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency> <!-- lombok 依赖,简化开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency> <!-- 这是 hspliving-common 需要的依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.12</version>
</dependency> <!-- 这是 hspliving-common 需要的依赖,
从 renren-generator 拷贝即可,指定 version -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency> <!--
解读
1. 引入 nacos 服务注册/ 发现
2. 以后,引入了 hspliving-common 的微服务模块就有了 nacos服务注册/发现依赖,不需要在引入 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies> <!--
解读
1. dependencyManagement是包依赖管理
2.该微服务模块的 pom.xml
的<dependencies></dependencies>引入 spring cloud alibaba
相关依赖的时候,就不需要指定版本了
3. 注意:这里仅仅只是声明了,并没有进入实际上的 依赖。
-->
<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>

最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

通用型产品发布解决方案(基于分布式微服务技术栈:SpringBoot+SpringCloud+Spring CloudAlibaba+Vue+ElementUI+MyBatis-Plus+MySQL+Git+Maven+Linux+Docker+Nginx - 《01》的更多相关文章

  1. 分布式微服务技术之 Spring Cloud Netflix

    1 背景 Netflix 是全球十大视频网站中唯一收费站点,是美国互联网流媒体播放商,由于访问量巨大,转型为云计算公司. 由Netflix公司主持开发了一套代码框架和库Netflix OSS即open ...

  2. 基于docker 如何部署surging分布式微服务引擎

    1.前言 转眼间surging 开源已经有1年了,经过1年的打磨,surging已从最初在window 部署的分布式微服务框架,到现在的可以在docker部署利用rancher 进行服务编排的分布式微 ...

  3. 推荐一款分布式微服务框架 Surging

    surging   surging 是一个分布式微服务框架,提供高性能RPC远程服务调用,采用Zookeeper.Consul作为surging服务的注册中心,集成了哈希,随机,轮询,压力最小优先作为 ...

  4. 我的分布式微服务框架:YC-Framework

    YC-Framework官方文档:http://framework.youcongtech.com/ YC-Framework源代码:https://github.com/developers-you ...

  5. Dapeng框架-开源高性能分布式微服务框架

    我们公司性质是新零售,公司也有专门的框架组.这群大牛自己开发了一整套分布式微服务框架.我们也在使用这套框架,有很多心得体会. 该框架既Dapeng也!开源github地址:https://github ...

  6. 整合SPRING CLOUD云服务架构 - 企业分布式微服务云架构构建

    整合SPRING CLOUD云服务架构 - 企业分布式微服务云架构构建 1.   介绍 Commonservice-system是一个大型分布式.微服务.面向企业的JavaEE体系快速研发平台,基于模 ...

  7. .Net Core 分布式微服务框架 - Jimu 添加 Swagger 支持

    系列文章 .Net Core 分布式微服务框架介绍 - Jimu .Net Core 分布式微服务框架 - Jimu 添加 Swagger 支持 一.前言 最近有空就优化 Jimu (一个基于.Net ...

  8. Surging 分布式微服务框架使用入门

    原文:Surging 分布式微服务框架使用入门 前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与S ...

  9. [转载]Surging 分布式微服务框架使用入门

    前言 本文非 Surging 官方教程,只是自己学习的总结.如有哪里不对,还望指正. 我对 surging 的看法 我目前所在的公司采用架构就是类似与Surging的RPC框架,在.NET 4.0框架 ...

  10. Thrift搭建分布式微服务1

    Thrift搭建分布式微服务 一.Thrift是什么? 关于Thrift的基本介绍,参看张善友的文章Thrift简介. 二.为什么使用微服务? 在公司的高速发展过程中,随着业务的增长,子系统越来越多. ...

随机推荐

  1. 一个WPF下的虚拟键盘实现

    给上位机触摸屏做一个虚拟键盘,这玩意儿不就是一排的网格里面放满button嘛 .说归这样说 依然还是有一堆细节需要你去处理的.不论如何 先画个键盘吧. 简单的从网上找个键盘位图做参照使用 4行Grid ...

  2. 一个 .NET 开源、免费、功能强大的Windows应用卸载神器

    前言 今天大姚给大家分享一个基于 .NET 开源(Apache License).免费.功能强大的Windows应用卸载神器:Bulk Crap Uninstaller. 项目介绍 Bulk Crap ...

  3. RPM常用命令以及组合使用场景

    本文分享自天翼云开发者社区<RPM常用命令以及组合使用场景>,作者:邬祥钊   当涉及到管理基于 Red Hat 系的 Linux 系统时,RPM (Red Hat Package Man ...

  4. 看图识WAF

    看图识WAF 宝塔 腾讯云T-Sec 阿里云盾 创宇盾

  5. Centos7搭建mailx邮件应用

    邮件发送原理图 邮件用户代理(MUA,Mail User Agent)邮件传送代理(MTA,Mail Transport Agent)邮件分发代理(MDA,Mail Deliver Agent) 邮件 ...

  6. 批量上传序列到Gisaid数据库

    本文演示的是用Gisaid官网自带的Excel宏批量上传毒株信息和序列到Gisaid数据库,需要魔法. 1. 下载GISAID Batch Uploader 找到自己需要上传的病毒类型,我是Flu,在 ...

  7. Zookeeper Java客户端连接慢、超时问题Ad-Hoc检查清单

    TL;DR 排查思路: 首先确认你的设备到zookeeper的连通性是OK的,可通过命令echo srvr | nc HOST 2181,检查是否可以正常打印节点信息.windows用户可以在命令行输 ...

  8. SpringBoot集成WebServlet出现自定义单servlet请求失败的问题

    一.导言 SpringBoot的真正核心是快速整合以及自动装配,所以在spring家族中springBoot不仅整合了Spring的IOC容器还兼容了WebServlet容器:这使得springBoo ...

  9. 使用word模板的科研论文编写

    编写SCD论文等的时候,可能出现官网的论文模板不够全面.一般我们使用latex作为论文编写模板,格式等都方便控制和编写,而word模板操作起来较为复杂.但是官网有些时候可能找不到latex的模板内容, ...

  10. 【Bug记录】defineEmits 在 TS 项目报错

    前言 单文件组件 <script setup> 是 Vue3.2 版本后非常流行的写法. 但是在开发过程中,按照官网使用 defineEmits ,结果项目报错,无法运行. 错误截图 Un ...