通用型产品发布解决方案(基于分布式微服务技术栈: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. AlertWindowManager 弹出提示窗口

    LookAndFeel(界面外观): NativeStyle:本地化界面为真实用系统内置外观 SkinName:本地化界面(NativeStyle:)设置为假可使用皮肤外观 OptionAnimate ...

  2. delphi cxgrid保存正在编辑的行

    procedure SaveGridViewEditing(AView: TcxGridDBTableView); overload; var vDst: TDataSet; begin // 应用未 ...

  3. mysql存储过程取得错误信息的方式。

    GET DIAGNOSTICS CONDITION 1 @v_sqlstate=RETURNED_SQLSTATE,@v_message= MESSAGE_TEXT; SELECT @v_sqlsta ...

  4. WitAwards 2024荣耀登榜!AOne载誉而归!

    近日,FCIS 2024网络安全创新大会在上海举办.本次大会以"迈向安全服务化时代"为主题,邀请来自全球的网安精英.技术专家.CISO/CSO.白帽子.创业者等展开深度对话,分享与 ...

  5. Windows安装MySql时出现“Failed to find valid data directory”的错误

    1.问题描述 具体错误信息如下所示: 2021-08-13T06:18:24.942954Z 0 [System] [MY-010116] [Server] D:\Net_Program\Net_My ...

  6. pip安装MySQLdb报错mysql_config not found

    报错EnvironmentError: mysql_config not found解决方法 1.sudo apt-get install python-setuptools 2.sudo apt-g ...

  7. MarkDown学习使用图片

    学习MarkDown使用

  8. 深入理解 Docker 容器技术

    一.引言 在当今的云计算和软件开发领域,Docker 容器技术已经成为了一项不可或缺的工具.它极大地改变了应用程序的部署和运行方式,为开发者和运维人员带来了诸多便利. 二.Docker 容器是什么? ...

  9. 使用 DeepSeek R1 和 Ollama 开发 RAG 系统

    1.概述 掌握如何借助 DeepSeek R1 与 Ollama 搭建检索增强生成(RAG)系统.本文将通过代码示例,为你提供详尽的分步指南.设置说明,分享打造智能 AI 应用的最佳实践. 2.内容 ...

  10. 解决win11输入法自定义短语有多个当前日期只有最后一个生效

    由于win11自定义短语保存机制问题导致只有最后一个当前日期短语生效** 1.首先找到自定义短语存储文件的具体路径:%AppData%\Microsoft\InputMethod\Chs\ 拼音输入法 ...