Compare commits
28 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
a5249db375 | |
|
|
9f524440e9 | |
|
|
cb6a00c4b8 | |
|
|
3601965980 | |
|
|
91325c062b | |
|
|
ba068cd992 | |
|
|
16a2cf35d0 | |
|
|
956333d270 | |
|
|
43d433828c | |
|
|
1148783dc3 | |
|
|
cedd346a7b | |
|
|
7a81a6d761 | |
|
|
64a388ea4c | |
|
|
f001dca4b0 | |
|
|
dade8a8c42 | |
|
|
0899b1b813 | |
|
|
aec78ec5cb | |
|
|
fe7b21ba53 | |
|
|
eee3ad9298 | |
|
|
ff7cef6835 | |
|
|
e83d809db9 | |
|
|
610d1856a0 | |
|
|
07d5f497bd | |
|
|
60fb006a61 | |
|
|
14090f5965 | |
|
|
bec66ef460 | |
|
|
0ce1729f6b | |
|
|
6594effa84 |
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
title: "Cloud Security",// 文档标题,左上角显示
|
||||
description: "Cloud Security",
|
||||
title: "FateVerse",// 文档标题,左上角显示
|
||||
description: "FateVerse | 缘境",
|
||||
markdown: {
|
||||
lineNumbers: true // 代码块显示行号
|
||||
},
|
||||
|
|
@ -20,30 +20,30 @@ module.exports = {
|
|||
// {text: "简体中文", link: "/language/chinese"}
|
||||
// ]
|
||||
// },
|
||||
{text: "博客", link: "https://blog.isww.cn/"}
|
||||
{text: "博客", link: "https://blog.odliken.cn/"}
|
||||
],
|
||||
/**
|
||||
* 设置侧边栏最大深度
|
||||
* 一般是以单个md文件中的 # ## ### #### 这几个标题文字为锚点自动生成导航
|
||||
* **/
|
||||
sidebarDepth: 4,
|
||||
sidebarDepth: 1,
|
||||
// sidebarDepth: 0, //0读取1级标题 1读取1级和2级标题 2读取1级、2级、3级标题
|
||||
// 设置侧边栏内容
|
||||
sidebar: [
|
||||
{
|
||||
title: '教程',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/document/',
|
||||
// {
|
||||
// title: '侧边栏组合',
|
||||
// // collapsable: true,
|
||||
// children: [
|
||||
// '/second/child/'
|
||||
// ]
|
||||
// }
|
||||
]
|
||||
},
|
||||
// {
|
||||
// title: '本文档使用教程',
|
||||
// collapsable: false,
|
||||
// children: [
|
||||
// '/document/',
|
||||
// // {
|
||||
// // title: '侧边栏组合',
|
||||
// // // collapsable: true,
|
||||
// // children: [
|
||||
// // '/second/child/'
|
||||
// // ]
|
||||
// // }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
title: '文档',
|
||||
collapsable: false,
|
||||
|
|
@ -52,6 +52,50 @@ module.exports = {
|
|||
'/wiki/back-build/'
|
||||
] // 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md
|
||||
},
|
||||
{
|
||||
title: '模块介绍',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/module/',
|
||||
'/module/common/',
|
||||
'/module/gateway/',
|
||||
'/module/auth/',
|
||||
'/module/umps/',
|
||||
'/module/notice/',
|
||||
// '/module/visual/',
|
||||
'/module/code-gen/',
|
||||
'/module/custom-query/',
|
||||
'/module/visual/',
|
||||
] // 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md
|
||||
},
|
||||
{
|
||||
title: '功能使用',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/use/spring-amin/',
|
||||
'/use/sentinel-dashboard/',
|
||||
] // 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md
|
||||
},
|
||||
{
|
||||
title: '工具类使用',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/util/tree-util/',
|
||||
'/util/excel/',
|
||||
] // 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md
|
||||
},
|
||||
{
|
||||
title: 'CI/CD',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/ci-cd/',
|
||||
'/ci-cd/gitea/',
|
||||
'/ci-cd/drone/',
|
||||
'/ci-cd/harbor/',
|
||||
'/ci-cd/rancher/',
|
||||
'/ci-cd/integration/',
|
||||
] // 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 7.0 KiB |
|
|
@ -4,11 +4,11 @@ heroImage: /logo.png
|
|||
actionText: 快速上手 →
|
||||
actionLink: /wiki/introduce/
|
||||
features:
|
||||
- title: 简洁至上
|
||||
details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
|
||||
- title: Vue驱动
|
||||
details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
|
||||
- title: 高性能
|
||||
details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
|
||||
footer: MIT Licensed | Copyright © 2018-present Evan You
|
||||
- title: ebts-cloud快速开发系统
|
||||
details: 以Spring Security作为核心搭建集群权限校验,多种功能模块提高开发效率。
|
||||
- title: 开发效率
|
||||
details: 以权限管理为核心,代码生成、自定义查询、服务监控、Swagger聚合文档、对象储存OSS的封装、Excel快速导出、Sentinel限流控制面板和Workflow工作流等模块为辅,提高系统的效率。
|
||||
- title: 专注业务
|
||||
details: 让开发人员更加关注系统业务的实现,花更多的时间在业务上。
|
||||
footer: Copyright © 2022-present Clay
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
# 关于CI/CD
|
||||
CI/CD 具有不同的含义, "CI"始终指持续集成,它属于开发人员的自动化流程。"CD"指的是持续交付和/或持续部署,这些相关概念有时会交叉使用。在现在的devops模式下,可以使用ci/cd持续集成对项目进行部署,通过,ci/cd中的一些环境变量则可以区分出对应的环境,以便于软件开发流程的控制。
|
||||
|
||||
## CI 持续集成(Continuous Integration)
|
||||
现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为"合并日"),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。
|
||||
|
||||
持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或"主干"中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。
|
||||
|
||||
> 现有的CI工具则有
|
||||
> - gitee (码云)
|
||||
> - GitHub
|
||||
> - gitlab (企业内部仓库)
|
||||
> - gitea (轻量化代码仓库)
|
||||
|
||||
## CD 持续部署(Continuous Deployment)
|
||||
对于一个成熟的 CI/CD 管道来说,最后的阶段是持续部署。作为持续交付——自动将生产就绪型构建版本发布到代码存储库——的延伸,持续部署可以自动将应用发布到生产环境。由于在生产之前的管道阶段没有手动门控,因此持续部署在很大程度上都得依赖精心设计的测试自动化。
|
||||
|
||||
实际上,持续部署意味着开发人员对应用的更改在编写后的几分钟内就能生效(假设它通过了自动化测试)。这更加便于持续接收和整合用户反馈。总而言之,所有这些 CI/CD 的关联步骤都有助于降低应用的部署风险,因此更便于以小件的方式(而非一次性)发布对应用的更改。不过,由于还需要编写自动化测试以适应 CI/CD 管道中的各种测试和发布阶段,因此前期投资还是会很大。
|
||||
|
||||
> 现有的CD工具
|
||||
> - jekenis (主流)
|
||||
> - drone (小众轻量化)
|
||||
> - gitlab (自带cd环境,需要配置一台外部的ranner执行构建任务)
|
||||
|
||||
## 集群部署环境k8s
|
||||
Kubernetes是Google 2014年创建管理的,是Google 10多年大规模容器管理技术Borg的开源版本。它是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。
|
||||
|
||||
在开发过程中,大多时候开发人员或运维人员并不会使用Kubernetes client 命令行工具,更多的是使用Kubernetes的可视化管理界面。
|
||||
> - kuboard
|
||||
> - Lens
|
||||
> - KubeSphere
|
||||
> - Wayne
|
||||
> - Kubernetes Dashboard
|
||||
> - Rancher
|
||||
|
||||
Kubernetes在应对分布式,集群管理的情况下可谓是的心应手。
|
||||
|
||||
|
||||
## 完整的CI/CD组合
|
||||
完整的ci/cd组合,即ci工具+cd工具联通,实现持续集成与持续部署的功能
|
||||
- gitee + jekenis + k8s
|
||||
- gitlab + jekenis + k8s
|
||||
- gitea + jekenis + k8s
|
||||
- gitea + drone + k8s
|
||||
|
||||
## 系统ci/cd
|
||||
本系统由于资源紧张则选用轻量化的组合,gitea+drone+k8s(rancher)
|
||||
下面会介绍每个组件的相关安装以及如何运用于本系统。
|
||||
另外,如需要参考ci/cd运用于其他的单体项目则可以参考
|
||||
|
||||
[Gitea+Drone+Rancher CI/CD持续集成解决方案](https://blog.odliken.cn/2022/08/07/giteadronerancher-ci-cd%e6%8c%81%e7%bb%ad%e9%9b%86%e6%88%90%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88/)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# Drone
|
||||
## 关于Drone
|
||||
Dron是一个现代化的持续集成平台,它使用强大的云原生pipeline引擎自动化构建、测试和发布工作流。Drone 与多个源代码管理系统无缝集成,包括 GitHub、GitHubEnterprise、Bitbucket、GitLab和Gitea;**它的每个构建都在一个隔离的 Docker 容器中运行**;另外它也支持插件,可以使用你熟知的语言轻松的扩展它们。
|
||||
## 安装
|
||||
### 依赖安装
|
||||
需要安装docker和docker-compose,参照上方安装方式即可
|
||||
### 安装Drone
|
||||
安装参考: [Gitea | Drone](https://docs.drone.io/server/provider/gitea/ "Gitea | Drone")
|
||||
|
||||
此处同样采用docker-compose.yml的方式安装
|
||||
```sh
|
||||
version: '3'
|
||||
services:
|
||||
drone-server:
|
||||
restart: always
|
||||
image: drone/drone:2
|
||||
ports:
|
||||
- "映射宿主机端口:80"
|
||||
volumes:
|
||||
- 宿主机挂载目录:/var/lib/drone/
|
||||
- 宿主机挂载目录:/data/
|
||||
environment:
|
||||
- DRONE_GITEA_SERVER=http://gitea服务器地址 # 支持http, https
|
||||
- DRONE_GITEA_CLIENT_ID=gitea生成的OAuth2客户端ID
|
||||
- DRONE_GITEA_CLIENT_SECRET=gitea生成的OAuth2客户端密钥
|
||||
- DRONE_SERVER_HOST=drone服务器地址
|
||||
- DRONE_SERVER_PROTO=http # 支持http, https
|
||||
- DRONE_RPC_SECRET=自定义的Drone与runner通信密钥
|
||||
- DRONE_GIT_ALWAYS_AUTH=true
|
||||
- DRONE_GIT_USERNAME=部署账户的用户名
|
||||
- DRONE_GIT_PASSWORD=部署账户的密码
|
||||
- DRONE_USER_CREATE=username:你的管理员账户名,admin:true # 开启管理员账户
|
||||
drone-runner-docker:
|
||||
restart: always
|
||||
image: drone/drone-runner-docker:1
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- DRONE_RPC_PROTO=http # 支持http, https
|
||||
- DRONE_RPC_HOST=drone-server
|
||||
- DRONE_RPC_SECRET=自定义的Drone与runner通信密钥
|
||||
- DRONE_RUNNER_NAME=drone-runner-docker
|
||||
- DRONE_RUNNER_CAPACITY=2
|
||||
```
|
||||
其中需要将gitea的授权信息填写到上方yml文件中
|
||||
|
||||
Gitea个人中心的应用设置创建Gitea OAuth application
|
||||

|
||||
点击创建后将秘钥妥善保管并替换到上面的docker-compose.yml
|
||||
```
|
||||
- DRONE_GITEA_CLIENT_ID=gitea生成的OAuth2客户端ID
|
||||
- DRONE_GITEA_CLIENT_SECRET=gitea生成的OAuth2客户端密钥
|
||||
- DRONE_GIT_USERNAME=令牌名称
|
||||
- DRONE_GIT_PASSWORD=令牌秘钥
|
||||
```
|
||||
生成Drone与runner通信密钥,并替换上面docker-compose.yml对应的字段
|
||||
```
|
||||
openssl rand -hex 16
|
||||
93b722f581830b9abf11345536b9ecfb
|
||||
```
|
||||
启动drone
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
### 访问drone
|
||||
访问:[http://drone-server-domain](http://server-ip:80/ "http://drone-server-domain")
|
||||
|
||||

|
||||
|
||||
授权
|
||||
|
||||

|
||||
|
||||
填写登录信息
|
||||
|
||||

|
||||
|
||||
登录之后就可以看到刚刚我们gitea中的项目
|
||||
|
||||

|
||||
|
||||
在设置中激活
|
||||
|
||||

|
||||
|
||||
激活保存
|
||||
|
||||

|
||||
|
|
@ -0,0 +1,122 @@
|
|||
## 关于Gitea
|
||||
Gitea 是一个自己托管的Git服务程序。他和GitHub, Bitbucket or Gitlab等比较类似。他是从 [Gogs](http://gogs.io/) 发展而来,Gitea 是一个开源社区驱动的轻量级代码托管解决方案,采用 MIT 许可证。极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了x86,amd64,还包括 ARM 和 PowerPC。
|
||||
## 系统要求
|
||||
- 最低的系统硬件要求为一个廉价的树莓派
|
||||
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存
|
||||
## [功能特性](https://docs.gitea.io/zh-cn/#%E5%8A%9F%E8%83%BD%E7%89%B9%E6%80%A7)
|
||||
- 支持活动时间线
|
||||
- 支持 SSH 以及 HTTP/HTTPS 协议
|
||||
- 支持 SMTP、LDAP 和反向代理的用户认证
|
||||
- 支持反向代理子路径
|
||||
- 支持用户、组织和仓库管理系统
|
||||
- 支持添加和删除仓库协作者
|
||||
- 支持仓库和组织级别 Web 钩子(包括 Slack 集成)
|
||||
- 支持仓库 Git 钩子和部署密钥
|
||||
- 支持仓库工单(Issue)、合并请求(Pull Request)以及 Wiki
|
||||
- 支持迁移和镜像仓库以及它的 Wiki
|
||||
- 支持在线编辑仓库文件和 Wiki
|
||||
- 支持自定义源的 Gravatar 和 Federated Avatar
|
||||
- 支持邮件服务
|
||||
- 支持后台管理面板
|
||||
- 支持 MySQL、PostgreSQL、SQLite3、MSSQL 和 TiDB(MySQL) 数据库
|
||||
- 支持多语言本地化(21 种语言)
|
||||
- 支持软件包注册中心(Composer/Conan/Container/Generic/Helm/Maven/NPM/Nuget/PyPI/RubyGems)
|
||||
## 安装
|
||||
### 安装环境依赖
|
||||
gitea 依赖于docker和docker-compose
|
||||
#### 安装docker
|
||||
安装所需的安装包yum-utils:
|
||||
```sh
|
||||
yum install -y yum-utils
|
||||
```
|
||||
添加yum源
|
||||
```sh
|
||||
yum -y update
|
||||
#设置镜像仓库地址
|
||||
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
|
||||
#更新yum软件包索引
|
||||
yum makecache fase
|
||||
```
|
||||
查看可用版本
|
||||
```sh
|
||||
yum list docker-ce --showduplicates | sort -r
|
||||
```
|
||||
安装docker社区版和社区办对应的cli工具已经依赖
|
||||
```sh
|
||||
yum install -y docker-ce docker-ce-cli containerd.io
|
||||
```
|
||||
启动 Docker 并设置开机自启
|
||||
```sh
|
||||
systemctl start docker
|
||||
systemctl enable docker
|
||||
```
|
||||
添加镜像源地址
|
||||
```
|
||||
tee /etc/docker/daemon.json <<-'EOF'
|
||||
{
|
||||
"registry-mirrors": ["https://uyah70su.mirror.aliyuncs.com"]
|
||||
}
|
||||
EOF
|
||||
```
|
||||
重新启动 Docker 服务
|
||||
```
|
||||
systemctl daemon-reload && sudo systemctl restart docker
|
||||
```
|
||||
验证配置是否生效
|
||||
```
|
||||
docker info | grep Mirrors -A1
|
||||
```
|
||||
#### 安装docker-compose
|
||||
安装epel源
|
||||
```
|
||||
yum install -y epel-release
|
||||
```
|
||||
安装docker-compose
|
||||
```
|
||||
yum install -y docker-compose
|
||||
```
|
||||
### 安装gitea
|
||||
安装参考链接:[使用 Docker 安装 - Docs](https://docs.gitea.io/zh-cn/install-with-docker/ "使用 Docker 安装 - Docs")
|
||||
|
||||
最简单的设置只是创建一个卷和一个网络,然后将 `gitea/gitea:latest` 镜像作为服务启动。由于没有可用的数据库,因此可以使用 SQLite3 初始化数据库。创建一个类似 `gitea` 的目录,并将以下内容粘贴到名为 `docker-compose.yml` 的文件中。
|
||||
```
|
||||
version: "3"
|
||||
|
||||
networks:
|
||||
gitea:
|
||||
external: false
|
||||
|
||||
services:
|
||||
server:
|
||||
image: gitea/gitea:1.16.7
|
||||
container_name: gitea
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
restart: always
|
||||
networks:
|
||||
- gitea
|
||||
volumes:
|
||||
- /home/data:/data # /home/data可以替换成你想要的挂载目录
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "3030:3000" # 3030可以替换成你想要的端口
|
||||
- "322:22" # 322可以替换成22
|
||||
```
|
||||
在后台启动
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
### 配置gitea
|
||||
访问:[http://server-ip:3030](http://server-ip:3030/ "http://server-ip:3030")
|
||||
|
||||
设置域名访问地址等信息
|
||||
|
||||

|
||||
|
||||
设置管理员账号密码
|
||||
|
||||

|
||||
|
||||
点击安装后,系统安装完毕会自动登录
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
# Harbor
|
||||
Harbor 是由 VMware 开源的一款云原生制品仓库,Harbor 的核心功能是存储和管理 Artifact。Harbor 允许用户用命令行工具对容器镜像及其他 Artifact 进行推送和拉取,并提供了图形管理界面帮助用户查看和管理这些 Artifact。在 Harbor 2.0 版本中,除容器镜像外,Harbor 对符合 OCI 规范的 Helm Chart、CNAB、OPA Bundle 等都提供了更多的支持。
|
||||

|
||||
|
||||
## 安装准备
|
||||
|
||||
1.需要安装docker和docker-compose并运行,docker安装可以参考:
|
||||
[Gitea+Drone+Rancher CI/CD持续集成解决方案](https://blog.odliken.cn/2022/08/07/giteadronerancher-ci-cd%e6%8c%81%e7%bb%ad%e9%9b%86%e6%88%90%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88/)中有详细解决方案
|
||||
|
||||
## 安装
|
||||
|
||||
下载地址:[https://github.com/goharbor/harbor/releases](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fgoharbor%2Fharbor%2Freleases)
|
||||
直接选择编译好的包
|
||||

|
||||
|
||||
这里有两个包Harbor offline installer 和 Harbor online installer,两者的区别的是 Harbor offline installer 里就包含的 Harbor 需要使用的镜像文件
|
||||
|
||||
下载成功,并解压
|
||||
```
|
||||
tar zxf harbor-offline-installer-v1.10.1.tgz -C /data/
|
||||
```
|
||||
|
||||
进入解压的目录,并 ls
|
||||
```
|
||||
[root@master ~]# cd /data/harbor/
|
||||
[root@master harbor]# ls
|
||||
common.sh harbor.v1.10.1.tar.gz harbor.yml install.sh LICENSE prepare
|
||||
```
|
||||
|
||||
## 编辑配置文件
|
||||
编辑 harbor.yml 配置文件,hostname 是 harbor 对外暴露的访问地址,HTTP 服务对外暴露 80 端口。这里可暂时先不配置HTTPS,可将HTTPS 相关内容注释。
|
||||
|
||||

|
||||
|
||||
修改完成后运行`./prepare `命令
|
||||
|
||||
# 部署
|
||||
```
|
||||
./install.sh
|
||||
```
|
||||
|
||||
## 登录页面
|
||||
浏览器输入 http://192.168.101.105 访问 Harbor 页面,用户名和密码为 harbor.yml 配置文件中默认设置的 admin,Harbor12345。
|
||||
|
||||

|
||||
|
||||
编辑 /etc/docker/daemon.json,设置允许访问的 HTTP 仓库地址。
|
||||
```
|
||||
{
|
||||
"insecure-registries":["11.8.36.21:8888"]
|
||||
}
|
||||
```
|
||||
## HTTPS 配置(可选)
|
||||
在生产环境中建议配置 HTTPS,可以使用由受信任的第三方 CA 签名的证书,也可以使用自签名证书。如果想要启用 Content Trust with Notary 来正确签名所有图像,则必须使用 HTTPS。
|
||||
### 生成 CA 证书
|
||||
|
||||
本次实验中我们使用自签名证书。生产环境中应使用受信任的第三方 CA 签名的证书。
|
||||
|
||||
### 生成 CA 证书私钥
|
||||
```
|
||||
openssl genrsa -out ca.key 4096
|
||||
```
|
||||
### 生成 CA 证书
|
||||
|
||||
-subj 表示证书的组织。CN 后面的值改成 harbor 的 IP 地址或者域名。
|
||||
```
|
||||
openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=11.8.36.21" \ -key ca.key \ -out ca.crt
|
||||
```
|
||||
### 生成 Server 证书
|
||||
|
||||
生成 Harbor 使用的证书和私钥。
|
||||
|
||||
### 生成 Server 私钥
|
||||
```
|
||||
openssl genrsa -out server.key 4096
|
||||
```
|
||||
### 生成 Server 证书签名请求(CSR)
|
||||
|
||||
生成 Harbor 的证书签名请求,使用上面生成的 CA 证书来给 Server 签发证书。
|
||||
|
||||
```
|
||||
openssl req -sha512 -new \
|
||||
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=11.8.36.21" \
|
||||
-key server.key \
|
||||
-out server.csr
|
||||
```
|
||||
### 生成 x509 v3 扩展文件
|
||||
|
||||
通过 docker 或者 ctr 等工具拉取 HTTPS 的镜像时,要求 HTTPS 的证书包含 SAN 扩展。
|
||||
|
||||
SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 [SSL 证书](https://cloud.tencent.com/product/symantecssl?from=10680),可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。例如下图中 Google 的这张证书的主题备用名称(SAN)中列了一大串的域名,因此这张证书能够被多个域名所使用。对于 Google 这种域名数量较多的公司来说,使用这种类型的证书能够极大的简化网站证书的管理。
|
||||
使用以下命令生成 x509 v3 扩展文件:
|
||||
```
|
||||
cat > v3.ext <<-EOF
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = IP:11.8.36.21
|
||||
EOF
|
||||
```
|
||||
|
||||
如果是域名访问通过下面方式生成 x509 v3 扩展文件:
|
||||
|
||||
```
|
||||
cat > v3.ext <<-EOF
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1=yourdomain.harbor.com
|
||||
EO
|
||||
```
|
||||
|
||||
### 使用 CA 证书签发 Server 证书
|
||||
|
||||
```
|
||||
openssl x509 -req -sha512 -days 3650 \
|
||||
-extfile v3.ext \
|
||||
-CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-in server.csr \
|
||||
-out server.crt
|
||||
```
|
||||
## 为 Harbor 和 Docker 配置证书
|
||||
|
||||
### 将 server 证书和密钥复制到 Harbor 主机上的 /data/cert 目录中
|
||||
### 转换 server.crt 为 server.cert
|
||||
|
||||
Docker 守护程序会认为 .crt 文件是 CA 证书,因此需要将 server 证书转换为 server.cert 文件。其实改下后缀就可以了,证书里面的内容是一样的。
|
||||
```
|
||||
openssl x509 -inform PEM -in server.crt -out server.cert
|
||||
```
|
||||
|
||||
### 重启 Docker Engine
|
||||
|
||||
```
|
||||
systemctl restart docker
|
||||
```
|
||||
### 重新部署 Harbor
|
||||
|
||||
修改 harbor.yml 配置文件,添加 HTTPS 相关配置,指定 HTTPS 的端口号和证书路径:
|
||||
### 使用 prepare 脚本生成 HTTPS 配置
|
||||
|
||||
使用 prepare 脚本为反向代理 Nginx 容器生成 HTTPS 配置。
|
||||
|
||||
```
|
||||
./prepare
|
||||
```
|
||||
### 删除原有 Harbor 容器
|
||||
|
||||
Harbor 原有的数据文件默认是挂载在宿主机的 /data 目录下,因此删除 Harbor 容器并不会丢失数据。
|
||||
|
||||
```
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
### 重新启动 Harbor
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
# 集成
|
||||
ebts-cloud快速开发系统采用前后端分离试开发,后端为Spring Cloud 体系架构,前端现有React,后期会继续更新Vue3.0版本前端页面
|
||||
|
||||
## gitea + drone 集成
|
||||
在安装drone的时候可知,drone需要获取到gitea的oauth2的授权,这样drone就可以通过携带秘钥信息去请求到gitea当前用户下的所有仓库了
|
||||
|
||||
当gitea中的仓库有request的时候,gitea会发送一个Web沟子请求到drone
|
||||

|
||||
|
||||
请求内容中就会包括到当前提交的相关信息,drone就可以拿到这些信息获取到gitea仓库中的最新代码
|
||||
|
||||
## drone + k8s(rancher)
|
||||
drone触发k8s是非常简单的,需要准备一个带有kubectl命令行工具的docker镜像,当drone的steps运行到了这一步了之后,只需要使用命令行工具运行:
|
||||
```shell
|
||||
kubectl apply -f deployment.yml -n dev --kubeconfig=/app/config/kubectl_conf.yml
|
||||
```
|
||||
其中 kubectl_conf.yml 则是k8s的连接配置文件,deployment.yml文件则是需要构建内容的配置文件
|
||||
|
||||
当命令成功运行后,即可在k8s中看到新构建的内容,但是现在看来drone只是触发了k8s的构建,但是k8s构建所需要的docker镜像现在还未知,在docker中有一种叫做docker镜像仓库的存在,我们制作好的docker镜像就可以推送到镜像仓库中,并将仓库中对应的docker pull路径放到deployment.yml文件中,可以由k8s pull下来。
|
||||
|
||||
## Harbor
|
||||
Harbor是一个docker的镜像仓库,他可以独立部署,在drone和k8s中当做粘合剂,当drone中构建好了对应docker镜像后,将docker镜像推送到harbor,然后将pull 的路径又放到k8s的deployment.yml文件中,这样全链路就打通了。
|
||||
|
||||
为了方便区分每一次drone提交的docker镜像都是不同的,并且在k8s的机制中,如果你的pull 地址完全相同,k8s认为你没有对镜像进行更新,所以需要一个随机的变量,那么在gitea中存在一个值叫做commit id,这个值是每一次提交的唯一标识符,所以也可以作为区分镜像版本的变量。
|
||||
|
||||
|
||||
## 前端
|
||||
在前端项目中,有这
|
||||
```
|
||||
.drone.yml -- drone ci/cd管道配置命令
|
||||
deployment.yml -- k8s部署文件
|
||||
default.conf -- Nginx的映射配置文件
|
||||
Dockerfile -- 将前端的打包内容构建docker镜像的文件
|
||||
docker.sh -- 进行docker打包,并且按照约定格式推送到Harbor仓库
|
||||
.dockerignore -- docker的ignore文件
|
||||
```
|
||||
|
||||
### .drone.yml文件内容详情
|
||||
|
||||
```yaml
|
||||
kind: pipeline # drone配置文件类型 pipeline管道
|
||||
type: docker # 使用对应steps对应的类型,drone是基于docker容器进行每一个步骤的
|
||||
name: security-react # 本次构建任务的名称
|
||||
|
||||
steps:
|
||||
- name: build-package-react # 当前steps的名称
|
||||
image: node:16.18.0 # 需要使用到的镜像,与react项目中packjson中配置的node版本一致
|
||||
volumes: # 将容器内容部的地址映射到宿主机上
|
||||
- name: cache # node构建是的缓存,防止多次下载,提高build速度
|
||||
path: /drone/src/node_modules
|
||||
- name: build # 内容挂在,将打包的好产物放到当前目录下,方便后续steps的时候
|
||||
path: /app/build
|
||||
commands: # 容器中需要执行的命令
|
||||
- export CI=false
|
||||
- rm -rf /app/build/react/* # 初始化需要使用的目录
|
||||
- cp deployment.yml /app/build/react/ # 将k8s构建文件cp到产物挂在目录
|
||||
- cp Dockerfile /app/build/react/ #
|
||||
- cp .dockerignore /app/build/react/
|
||||
- cp default.conf /app/build/react/
|
||||
- cp docker.sh /app/build/react/
|
||||
- cp nginx.conf /app/build/react/
|
||||
- mkdir -p ./node_modules # 创建node依赖目录
|
||||
- export NODE_MODULES_PATH=`pwd`/node_modules # 构建与缓存目录之间的关联
|
||||
# - npm config set registry https://registry.npm.taobao.org # 可以开启淘宝npm的镜像加速
|
||||
# - set NODE_OPTIONS=--openssl-legacy-provider
|
||||
- npm install # 下载依赖,虽然已经进行了依赖的缓存,但是一旦有新的依赖加入,缓存中没有则无法构建,所以还需要install一下
|
||||
- npm run build # 开始构建产物
|
||||
- mkdir -p /app/build/react # 创建存放产物的映射目录
|
||||
- cp -r build /app/build/react # 将打包好之后的内容放入的对应的目录中
|
||||
|
||||
|
||||
- name: build-docker # 制作docker镜像
|
||||
image: docker # 使用官方docker镜像
|
||||
volumes: # 将容器内目录挂载到宿主机
|
||||
- name: build
|
||||
path: /app/build
|
||||
- name: docker # 挂载宿主机的docker
|
||||
path: /var/run/docker.sock
|
||||
- name: config
|
||||
path: /config
|
||||
environment: # 获取到密文的docker用户名和密码
|
||||
DOCKER_USERNAME:
|
||||
from_secret: docker_username
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
REGISTRY:
|
||||
from_secret: registry
|
||||
REGISTRY_NAMESPACE:
|
||||
from_secret: registry_namespace
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- cd /app/build/react/
|
||||
- cat Dockerfile
|
||||
# 将一下文件中的密文信息替换成对应的内容
|
||||
- sed -i 's/$REGISTRY/'"$REGISTRY"'/' deployment.yml
|
||||
- sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
|
||||
- sed -i 's/$DRONE_REPO_NAME/'"$DRONE_REPO_NAME"'/' deployment.yml
|
||||
- sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' deployment.yml
|
||||
# docker登录,不能在脚本中登录,并且不能使用docker login -u -p
|
||||
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
|
||||
- chmod +x docker.sh
|
||||
- sh docker.sh # 运行docker打包脚本
|
||||
# 执行完脚本删除本次制作的docker镜像,避免多次后当前runner空间不足
|
||||
- docker rmi -f $(docker images | grep $DRONE_REPO_NAME | awk '{print $3}')
|
||||
|
||||
- name: drone-rancher # rancher运行
|
||||
image: registry.cn-hangzhou.aliyuncs.com/claywang/kubectl #阿里云的kubectl镜像,里面包含kubectl命令行工具
|
||||
volumes: # 将容器内目录挂载到宿主机
|
||||
- name: build
|
||||
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
|
||||
- name: config
|
||||
path: /app/config # 将kubectl 配置文件挂载出来
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- cd /app/build/react/
|
||||
# 将deployment中定义的变量替换为drone中的内置变量
|
||||
- cat deployment.yml
|
||||
# 通过kubectl指令运行deployment.yml,并指定授权文件kubectl_conf.yml
|
||||
- kubectl apply -f deployment.yml -n dev --kubeconfig=/app/config/kubectl_conf.yml
|
||||
|
||||
|
||||
volumes:
|
||||
- name: build
|
||||
host:
|
||||
path: /home/build
|
||||
- name: cache
|
||||
host:
|
||||
path: /var/lib/npm/cache
|
||||
- name: config # k8s对接的配置文件
|
||||
host:
|
||||
path: /.kube/config
|
||||
- name: maven-cache # maven的缓存文件
|
||||
host:
|
||||
path: /home/data/maven/cache
|
||||
- name: docker # 宿主机中的docker
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
```
|
||||
|
||||
## 后端
|
||||
后端采用的为maven聚合工程,情况要比前端负责一些,下面是后端聚合工程的模块划分:
|
||||
```lua
|
||||
security-cloud
|
||||
├── auth -- 授权服务模块
|
||||
├── gateway -- 网关服务模块
|
||||
└── common -- 系统公共模块
|
||||
├── common-core -- 公共工具类核心包
|
||||
├── common-mybatis -- mybatis 扩展封装
|
||||
├── common-security -- 安全工具类
|
||||
├── common-swagger -- 接口文档
|
||||
├── common-feign -- feign 扩展封装
|
||||
└── common-log -- 系统日志记录
|
||||
└── upms -- 通用用户权限管理模块
|
||||
└── upms-api -- 通用用户权限管理系统公共api模块
|
||||
└── upms-biz -- 通用用户权限管理系统业务处理模块[4000]
|
||||
└── visual -- 图形化管理模块
|
||||
└── monitor -- 服务监控
|
||||
└── codegen -- 图形化代码生成
|
||||
```
|
||||
|
||||
现阶段需要进行ci/cd构建的模块有 auth(授权服务模块) ,umps-biz(通用用户权限管理系统业务处理模块) ,gateway(网关服务模块);在这样的前提下,后端就不能同前端一样,一个.drone.yml文件只是服务于单独的一个镜像构建打包了,在后端的情况下则需要通过其他方式来区分每一个模块,并对每一个模块进行从打包构建到发布的全部流程;所以ci/cd配置难度将加大。
|
||||
|
||||
在整个ci/cd活动中唯一能够区分他们并且能够伴随到整个ci/cd流程的只有分支名称,所以通过分支名称作为他们的发布名称,比如auth(授权服务模块)的发布名称就叫做auth,同时项目的jar包名称,以及后续的docker镜像名称,还有k8s的发布名称都需要统一;
|
||||
|
||||
在drone中用于储存分支名称的变量为$DRONE_COMMIT_BRANCH 需要了解其他drone变量则可以前往 [Drone内置变量](https://blog.odliken.cn/2022/08/06/drone%e5%86%85%e7%bd%ae%e5%8f%98%e9%87%8f/)
|
||||
|
||||
在编写ci/cd的过程中,则使用该变量贯穿整个ci/cd的流程,配合commit id实现区分不同的版本。
|
||||
|
||||
并且在获取分支的时候,直接cd $DRONE_COMMIT_BRANCH即可进入到对应模块下,然后找到target下$DRONE_COMMIT_BRANCH.jar,就可以将这个jar包放到构建的缓存路径下,进行下一步的操作了。
|
||||
|
||||
## 模块下的模块构建方式
|
||||
上方的结构图中出现了umps-biz模块,这个模块是在umps下方,他的分支名应该是什么呢?
|
||||
|
||||
当前模块的分支名当然是umps-biz,但是cd $DRONE_COMMIT_BRANCH的时候,真正的运行命令则是 cd umps-biz,在项目路径中没有当前的路径,所以这是我们需要使用到drone的when 关键词区分分支名称,分支为umps-biz的时候,我们需要将cd $DRONE_COMMIT_BRANCH 改为cd umps/当前模块的分支名当然是umps-biz,但是cd $DRONE_COMMIT_BRANCH的时候,真正的运行命令则是 cd umps-biz,在项目路径中没有当前的路径,所以这是我们需要使用到drone的when 关键词区分分支名称,分支为umps-biz的时候,我们需要将cd $DRONE_COMMIT_BRANCH 改为$DRONE_COMMIT_BRANCH,这样就可以继续完成下面的steps了
|
||||
|
||||
## .drone.yml文件详细
|
||||
```yaml
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: security-cloud
|
||||
steps:
|
||||
- name: build-jar # 流水线名称
|
||||
image: maven:3.8.5-openjdk-8 # 定义创建容器的Docker镜像,maven:3.8.5-openjdk-8用于对java进行打包使用
|
||||
volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
|
||||
- name: maven-cache
|
||||
path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载
|
||||
- name: maven-build
|
||||
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- rm -rf /app/build/*
|
||||
- mvn clean package -DskipTests=true # 应用打包命
|
||||
- cp start.sh /app/build/ # java包的启动脚本
|
||||
- cp Dockerfile /app/build/ # docker镜像打包时的文件
|
||||
- cp deployment.yml /app/build/ # k8s部署文件
|
||||
- cp .dockerignore /app/build/ # docker镜像打包时过滤文件
|
||||
- cp docker.sh /app/build/ # docker 镜像打包脚本
|
||||
- cd $DRONE_COMMIT_BRANCH
|
||||
- cp target/$DRONE_COMMIT_BRANCH.jar /app/build/$DRONE_COMMIT_BRANCH.jar # 将打包后的内容复制到挂载目录下
|
||||
when:
|
||||
branch:
|
||||
- gateway
|
||||
- auth
|
||||
|
||||
- name: build-jar-umps-biz # 流水线名称
|
||||
image: maven:3.8.5-openjdk-8 # 定义创建容器的Docker镜像,maven:3.8.5-openjdk-8用于对java进行打包使用
|
||||
volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
|
||||
- name: maven-cache
|
||||
path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载
|
||||
- name: maven-build
|
||||
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- rm -rf /app/build/*
|
||||
- mvn clean package -DskipTests=true # 应用打包命
|
||||
- cp start.sh /app/build/ # java包的启动脚本
|
||||
- cp Dockerfile /app/build/ # docker镜像打包时的文件
|
||||
- cp deployment.yml /app/build/ # k8s部署文件
|
||||
- cp .dockerignore /app/build/ # docker镜像打包时过滤文件
|
||||
- cp docker.sh /app/build/ # docker 镜像打包脚本
|
||||
- cd umps/$DRONE_COMMIT_BRANCH
|
||||
- cp target/$DRONE_COMMIT_BRANCH.jar /app/build/$DRONE_COMMIT_BRANCH.jar # 将打包后的内容复制到挂载目录下
|
||||
when:
|
||||
branch:
|
||||
- umps-biz
|
||||
|
||||
|
||||
|
||||
- name: build-docker # 制作docker镜像
|
||||
image: docker # 使用官方docker镜像
|
||||
volumes: # 将容器内目录挂载到宿主机
|
||||
- name: maven-build
|
||||
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
|
||||
- name: docker
|
||||
path: /var/run/docker.sock # 挂载宿主机的docker
|
||||
environment: # 获取到密文的docker用户名和密码
|
||||
DOCKER_USERNAME:
|
||||
from_secret: docker_username
|
||||
DOCKER_PASSWORD:
|
||||
from_secret: docker_password
|
||||
REGISTRY:
|
||||
from_secret: registry
|
||||
REGISTRY_NAMESPACE:
|
||||
from_secret: registry_namespace
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- cd /app/build
|
||||
- sed -i 's/$REGISTRY/'"$REGISTRY"'/' deployment.yml
|
||||
- sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
|
||||
- sed -i 's/$DRONE_COMMIT_BRANCH/'"$DRONE_COMMIT_BRANCH"'/' start.sh
|
||||
- sed -i 's/$DRONE_COMMIT_BRANCH/'"$DRONE_COMMIT_BRANCH"'/' Dockerfile
|
||||
- sed -i 's/$DRONE_COMMIT_BRANCH/'"$DRONE_COMMIT_BRANCH"'/' .dockerignore
|
||||
- sed -i 's/$DRONE_COMMIT_BRANCH/'"$DRONE_COMMIT_BRANCH"'/' deployment.yml
|
||||
- sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' deployment.yml
|
||||
# docker登录,不能在脚本中登录,并且不能使用docker login -u -p
|
||||
- cat Dockerfile
|
||||
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
|
||||
- chmod +x docker.sh
|
||||
- sh docker.sh
|
||||
# 执行完脚本删除本次制作的docker镜像,避免多次后当前runner空间不足
|
||||
- docker rmi -f $(docker images | grep $DRONE_COMMIT_BRANCH | awk '{print $3}')
|
||||
|
||||
|
||||
- name: drone-rancher # rancher运行
|
||||
image: registry.cn-hangzhou.aliyuncs.com/claywang/kubectl #阿里云的kubectl镜像,里面包含kubectl命令行工具
|
||||
volumes: # 将容器内目录挂载到宿主机
|
||||
- name: maven-build
|
||||
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
|
||||
- name: config
|
||||
path: /app/config # 将kubectl 配置文件挂载出来
|
||||
commands: # 定义在Docker容器中执行的shell命令
|
||||
- cd /app/build
|
||||
# 通过kubectl指令运行deployment.yml,并指定授权文件kubectl_conf.yml
|
||||
# - kubectl apply -f deployment.yml -n $DRONE_COMMIT_BRANCH --kubeconfig=/app/config/kubectl_conf.yml
|
||||
- kubectl apply -f deployment.yml -n dev --kubeconfig=/app/config/kubectl_conf.yml
|
||||
|
||||
|
||||
|
||||
volumes: # 定义流水线挂载目录,用于共享数据
|
||||
- name: maven-build # maven打包后的文件
|
||||
host:
|
||||
path: /home/data/maven/build # 从宿主机中挂载的目录
|
||||
- name: config # k8s对接的配置文件
|
||||
host:
|
||||
path: /.kube/config
|
||||
- name: maven-cache # maven的缓存文件
|
||||
host:
|
||||
path: /home/data/maven/cache
|
||||
- name: docker # 宿主机中的docker
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
# 定义触发条件
|
||||
trigger:
|
||||
branch:
|
||||
- gateway
|
||||
- auth
|
||||
- umps-biz
|
||||
event:
|
||||
- push
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
|
@ -0,0 +1,50 @@
|
|||
# Rancher
|
||||
Rancher 是供采用容器的团队使用的完整软件堆栈。它解决了管理多个Kubernetes集群的运营和安全挑战,并为DevOps团队提供用于运行容器化工作负载的集成工具。简单来说是一个k8s的管理软件!
|
||||
## 安装
|
||||
rancher的latest版本是2.6以上,本次安装v2.4.15
|
||||
```
|
||||
docker run -d --privileged --restart=unless-stopped -p 80:80 -p 443:443 --privileged rancher/rancher:v2.4.15
|
||||
```
|
||||
访问(注意是https)
|
||||
https://ip-server
|
||||
输入admin 的密码
|
||||
|
||||

|
||||
|
||||
点击下一步,设置rancher的访问url,如果有域名则填写域名
|
||||
|
||||

|
||||
|
||||
进入管理页面
|
||||
|
||||

|
||||
|
||||
## 添加集群
|
||||
点击右上角的添加集群
|
||||
|
||||

|
||||
|
||||
选择自定义
|
||||
|
||||

|
||||
|
||||
配置集训信息
|
||||
|
||||

|
||||
|
||||
点击下一步进入添加节点页面,由于当前集群还没有节点,所以主机选项中的每一个角色都需要选择
|
||||
|
||||

|
||||
|
||||
复制下方的命令到装有docker的主机中运行即可,注意rancher安装的主机和其他节点主机的**ip都必须为静态ip**,不然主机重启后集群就失效了,原因为主机重启后ip变更,k8s不能够通过ip找到对应的节点
|
||||
|
||||

|
||||
|
||||
集群正在添加节点(等待时间很漫长)
|
||||
|
||||

|
||||
|
||||
添加完成
|
||||
|
||||

|
||||

|
||||
|
|
@ -1,4 +1,125 @@
|
|||
# 首先
|
||||
# config.js配置教程
|
||||
|
||||
## 其次
|
||||
固定侧边栏,不折叠显示: collapsable: false,
|
||||
## 设置站点根路径
|
||||
- 如果访问的时候是ip+端口,那么这里就是**/**,如果访问的是ip+端口+工程名,那么这里就是 **/工程名/**
|
||||
- 这是部署到github相关的配置
|
||||
|
||||
```
|
||||
module.exports = {
|
||||
base:'/',
|
||||
}
|
||||
```
|
||||
|
||||
## 设置编译后的输出目录
|
||||
- **./ROOT**代表在工程的根目录下生成一个ROOT文件,里面是编译好的文件,可以拿ROOT直接部署。
|
||||
|
||||
```
|
||||
module.exports = {
|
||||
dest:'./ROOT',
|
||||
}
|
||||
```
|
||||
|
||||
## 本地调试的端口号,默认是8080
|
||||
|
||||
```
|
||||
module.exports = {
|
||||
port:8086,
|
||||
}
|
||||
```
|
||||
|
||||
## 代码块显示行号
|
||||
|
||||
```
|
||||
module.exports = {
|
||||
markdown: {
|
||||
lineNumbers: true // 代码块显示行号
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 页面路由地址对应写法
|
||||
|
||||
| 文件的相对路径 | 页面路由地址 |
|
||||
| :------------ | :------------ |
|
||||
| /README.md | / |
|
||||
| /guide/README.md | /guide/ |
|
||||
| /config.md | /config.html |
|
||||
|
||||
## 网页标签的图标
|
||||
- 这里的 '/' 指向 docs/.vuepress/public 文件目录 ,即 docs/.vuepress/public/logo.png
|
||||
|
||||
```
|
||||
themeConfig: {
|
||||
head: [
|
||||
['link', {rel: 'icon', href: '/logo.png'}],
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 导航栏Logo
|
||||
|
||||
```
|
||||
themeConfig: {
|
||||
logo:'/logo.png',
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## nav配置【右上角的导航条】
|
||||
|
||||
```
|
||||
themeConfig: {
|
||||
nav:
|
||||
[
|
||||
{text: "首页", link: "/"},
|
||||
{text: "指南", link: "/wiki/introduce/"},
|
||||
{text: "博客", link: "https://blog.isww.cn/"}
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## 侧边栏配置
|
||||
### 设置左侧导航显示的层级
|
||||
- sidebarDepth: 2
|
||||
- 一般是以单个md文件中的 # ## ### #### 这几个标题文字为锚点自动生成导航
|
||||
- 0读取1级标题
|
||||
- 1读取1级和2级标题
|
||||
- 2读取1级、2级、3级标题
|
||||
|
||||
### 设置侧边栏内容
|
||||
- 根据自己的需求来订,对应自己在docs下的文件夹名,默认首页是README.md 【/wiki/introduce/ 相当于 /wiki/introduce/README.md】效果如下图:
|
||||
- 【注】其中侧边栏除了config.js下的title外的导航栏目是根据该目录(例如:/wiki/introduce/)下的一级、二级、三级和四级标题所自动生成的导航栏目,所以**侧边栏只需要在config.js配置页面路由地址及title即可**
|
||||
|
||||
```
|
||||
themeConfig: {
|
||||
sidebar: [
|
||||
{
|
||||
title: '文档',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/wiki/introduce/',
|
||||
'/wiki/back-build/',
|
||||
{
|
||||
title: "侧边栏组合",
|
||||
collapsable: false,
|
||||
children: [
|
||||
"/second/child/"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 底部设置最后更新时间
|
||||
|
||||
```
|
||||
themeConfig: {
|
||||
//最后更新时间
|
||||
lastUpdated: 'Last Updated',
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
# 第一个侧边栏
|
||||
|
||||
firstside
|
||||
|
||||
firstside
|
||||
|
||||
## 二级第一个侧边栏
|
||||
|
||||
二级第一个侧边栏
|
||||
二级第一个侧边栏
|
||||
|
||||
## 二级第二个侧边栏
|
||||
|
||||
二级第二个侧边栏
|
||||
二级第二个侧边栏
|
||||
|
||||
### 三级第一个侧边栏
|
||||
三级第一个侧边栏
|
||||
三级第一个侧边栏
|
||||
三级第一个侧边栏
|
||||
|
||||
## 二级第三个侧边栏
|
||||
二级第三个侧边栏
|
||||
二级第三个侧边栏
|
||||
二级第三个侧边栏
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# 模块信息
|
||||
|
||||
> 以下是security-cloud所以模块,本章节将对其中的每一个模块进行详细的介绍。
|
||||
|
||||
```lua
|
||||
security-cloud
|
||||
├── auth -- 授权服务提供
|
||||
└── common -- 系统公共模块
|
||||
├── common-core -- 公共工具类核心包
|
||||
├── common-dubbo -- dubbo rpc服务
|
||||
├── common-email -- 邮件发送服务
|
||||
├── common-file -- 分布式文件存储
|
||||
├── common-mybatis -- mybatis 扩展封装
|
||||
├── common-redis -- redis序列化封装
|
||||
├── common-security -- 安全工具类
|
||||
├── common-swagger -- swagger接口文档
|
||||
├── common-log -- 系统日志记录
|
||||
└── common-websocket -- netty集群实现的websocket服务
|
||||
└── notice -- 通用消息公告模块
|
||||
├── notice-api -- 通用消息公告模块公共api模块
|
||||
└── notice-biz -- 通用消息公告模块业务处理模块[5000]
|
||||
└── upms -- 通用用户权限管理模块
|
||||
├── upms-api -- 通用用户权限管理系统公共api模块
|
||||
└── upms-biz -- 通用用户权限管理系统业务处理模块[4000]
|
||||
└── visual -- 图形化管理模块
|
||||
├── code-gen -- 代码生成模块
|
||||
├── custom-query -- 自定义查询模块
|
||||
├── flowable -- flowable实现的workflow模块
|
||||
├── monitor -- 服务监控
|
||||
├── sentinel-dashboard -- sentinel 官方版
|
||||
├── sentinel-dashboard-pro -- sentinel 线上版,集成nacos
|
||||
└── xxl-job-admin -- 定时任务管理器
|
||||
```
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# 授权服务模块
|
||||
## 服务简介
|
||||
授权登录服务是集群权限授权的入口,集群中的所有授权请求读应该在当前服务进行实现.
|
||||
|
||||
## 提供服务
|
||||
- 验证码获取
|
||||
- 账号密码登录
|
||||
- 微信小程序授权登录(规划中)
|
||||
- 用户登录后用户权限标识和用户信息获取
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# 代码生成
|
||||
## 简介
|
||||
代码生成模块适配MySql,Oracle等多种关系型数据库,可快速导入数据库表格元数据生成对应的单表crud前后端代码
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
# 系统公共模块
|
||||
系统公共模块主要对集群中的基础公共工具进行封装,提供更加高效的开发效率
|
||||
|
||||
## common-core
|
||||
### 简介
|
||||
对系统中的基础服务实体进行定义,基础的工具类进行定义.例如:list转Tree工具类,反射方式获取到用户信息等
|
||||
|
||||
### TreeUtil
|
||||
自定义树形结构实体转换,可以将A对象结构具有树形关系的list转换成B对象的树形结构, 可以自定义A B两个对象之间的映射关系例如:
|
||||
|
||||
将Dept映射到OptionTree对象:
|
||||
```javascript
|
||||
//Dept 类
|
||||
public class Dept extends BaseEntity {
|
||||
private Long deptId;
|
||||
private Long parentId;
|
||||
private String ancestors;
|
||||
@NotBlank(message = "部门名称不能为空!")
|
||||
private String deptName;
|
||||
@NotBlank(message = "显示顺序不能为空!")
|
||||
private String orderNum;
|
||||
@NotBlank(message = "负责人不能为空!")
|
||||
private String leader;
|
||||
@NotNull(message = "负责人id不能为空!")
|
||||
private Long leaderId;
|
||||
}
|
||||
|
||||
//OptionTree 目标对象
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OptionTree implements Serializable {
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Object value;
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String label;
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<OptionTree> children;
|
||||
}
|
||||
|
||||
//转换代码
|
||||
List<OptionTree> optionTreeList = TreeUtil.build(deptList,OptionTree.class,(config)->{
|
||||
config.setIdField("deptId");
|
||||
config.setOption("deptId","deptName");
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
并且TreeUtil可以通过config进行自定义配置,配置项如下:
|
||||
- 是否启用BeanUtils.copyProperties方法进行字段的copy(默认为ture)
|
||||
- 父级关系字段名称定义
|
||||
- id字段定义
|
||||
- tree子级字段定义
|
||||
- mapper其余字段关系映射
|
||||
- exclude排除字段
|
||||
|
||||
## common-dubbo
|
||||
### 简介
|
||||
对dubbo的基础依赖进行引入,后期进行全局的dubbo功能封装
|
||||
|
||||
## common-log
|
||||
### 简介
|
||||
自定义操作日志模块,完成使用自定义注解`@Log`实现操作日志的记录
|
||||
`@Log`注解参数说明
|
||||
|
||||
| 参数 | 类型 | 默认 | 描述|
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| title | String | 空 | 日志记录名称 |
|
||||
| businessType | BusinessType | OTHER | 功能,具体操作有 OTHER,INSERT,UPDATE,DELETE,GRANT,EXPORT,IMPORT,FORCE,GENCODE,CLEAN,BIND |
|
||||
| operatorType | OperateType | MANAGE | 操作人类别 |
|
||||
| isSaveRequestData | boole | 空 | 是否保存请求的参数 |
|
||||
|
||||
### 使用
|
||||
```javascript
|
||||
@ApiOperation("新增部门")
|
||||
@PostMapping
|
||||
@PreAuthorize("@ss.hasPermission('admin:dept:add')")
|
||||
@Log(title = "新增部门",businessType = BusinessType.INSERT)
|
||||
public Result<Void> add(@RequestBody @Validated Dept dept){
|
||||
if (UserConstants.DEPT_DISABLE.equals(deptService.checkDeptNameUnique(dept))){
|
||||
return Result.error("新增部门: "+ dept.getDeptName() +"'失败,部门名称以存在!");
|
||||
}
|
||||
deptService.saveDept(dept);
|
||||
return Result.ok();
|
||||
}
|
||||
```
|
||||
通过LogAspect实现当前注解的apo切面,切面中将操作的日志封装成一个操作日志对象,然后使用spring的事件发布机制发布这个日志操作对象,再使用OperationLogListener异步监听到发布的事件,再通过rpc远程调用到umps中的日志存储接口,将当前对象落地到数据库
|
||||
|
||||
|
||||
## common-mybatis
|
||||
### 简介
|
||||
对mybatis的分页插件进行封装,使用注解+aop+mybatis拦截器实现自动注入用户信息或者时间
|
||||
|
||||
### 分页操作
|
||||
只需要在执行SQL的Mapper之前调用`PageUtils.startPage()`方法,然后调用SQL执行方法,再使用获取到的数组调用PageUtils.getDataTable(list)方法获取到封装好的返回结果对象即。
|
||||
|
||||
- 代码演示
|
||||
```java
|
||||
@Override
|
||||
public TableDataInfo<DemoVo> searchDemoList(DemoQuery query) {
|
||||
PageUtils.startPage();
|
||||
List<Demo> list = demoMapper.selectDemoList(query);
|
||||
Long total = PageUtils.getTotal(list);
|
||||
List<DemoVo> convertList = list.stream().map(DemoVo::toDemoVo).collect(Collectors.toList());
|
||||
return PageUtils.convertDataTable(convertList, total);
|
||||
}
|
||||
```
|
||||
> 注意,上方代码为需要将Demo 转换为 DemoVo 如果无需转换,则可直接调用`PageUtils.getDataTable(list)`方法
|
||||
|
||||
### 自动注入能力
|
||||
自动注入使用场景为,当实体累中在特定的时间或者业务之下,需要设置一些固定的参数时,可以使用到此功能,列如当实体类中存在createBy,createTime,updateBy,updateTime等固定字段,在创建和更新的时候都需要固定的设置参数时,可以使用到此功能
|
||||
|
||||
- `@EnableAutoField` 开启mybatis的自动注入能力
|
||||
- `@GenerateId` 自动生成id并注入
|
||||
- `@AutoUser` 自动注入一些用户信息
|
||||
- `@AutoTime` 自动注入时间
|
||||
|
||||
#### `@GenerateId`注解参数说明
|
||||
|参数 | 类型 | 默认 | 描述|
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| idType | GenIdEnum | UUID | 需要生成的id类型 |
|
||||
|
||||
`@GenerateId`为实现需要指定id时候使用,可以自动生成指定类型的id,并注入其中,支持UUID的生成
|
||||
|
||||
#### `@AutoUser`注解参数说明
|
||||
|参数 | 类型 | 默认 | 描述|
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| value | AutoUserEnum | USER_NAME | 需要注入的用户信息 |
|
||||
| method | MethodEnum | 空 | 指定SQL执行方法下进行自动注入 |
|
||||
`@AutoUser`可以根据用户需要设置那种用户信息和具体SQL执行的那种类型的方法情况下自动注入需要的信息
|
||||
|
||||
#### `@AutoTime`注解参数说明
|
||||
|参数 | 类型 | 默认 | 描述|
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| method | MethodEnum | 空 | 指定SQL执行方法下进行自动注入 |
|
||||
`@AutoTime`可以根据SQL执行的方法类型进行匹配,自动创建时间对象并注入
|
||||
|
||||
## common-file
|
||||
### 简介
|
||||
在集群环境下,文件不能够单独放置在某一个单一节点执行,需要集中存储起来,我们提供了阿里云的oss,自建服务的minio以及轻量化的ftp三种模式
|
||||
### 使用
|
||||
1. 将依赖引入到对应的pom.xml中
|
||||
```xml
|
||||
<!--文件组件-->
|
||||
<dependency>
|
||||
<groupId>cn.odliken</groupId>
|
||||
<artifactId>common-file</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 在yml文件中配置好信息
|
||||
```yml
|
||||
file:
|
||||
store:
|
||||
minio:
|
||||
endpoint: endpoint
|
||||
bucket: bucket
|
||||
access-key: access-key
|
||||
secret-key: secret-key@2023
|
||||
aliyun:
|
||||
endpoint: endpoint
|
||||
#AccessId 和 AccessKey
|
||||
access-key-id: access-key-id
|
||||
secret-access-key: secret-access-key
|
||||
#创建的Bucket
|
||||
bucket: bucket
|
||||
ftp:
|
||||
address: address
|
||||
port: port
|
||||
username: username
|
||||
password: password
|
||||
encoding: UTF-8
|
||||
# 静态资源路径
|
||||
asset: asset
|
||||
# 公共文件资源
|
||||
pubfiles: pubfiles
|
||||
# 需要保密的文件资源
|
||||
prifiles: prifiles
|
||||
```
|
||||
3. 注入`FileStoreService`接口,需要指定使用那种类型的文件储存,也可以直接注入具体的实现类
|
||||
|
||||
```java
|
||||
/**
|
||||
* 文件存储服务接口类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-16
|
||||
*/
|
||||
public interface FileStoreService {
|
||||
|
||||
/**
|
||||
* 创建文件桶
|
||||
*
|
||||
* @param bucketName 桶名
|
||||
* @return 创建结果
|
||||
*/
|
||||
Boolean createBucket(String bucketName);
|
||||
|
||||
/**
|
||||
* 删除文件桶
|
||||
*
|
||||
* @param bucketName 桶名
|
||||
* @return 删除结果
|
||||
*/
|
||||
Boolean deleteBucket(String bucketName);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 文件对象
|
||||
* @return FileInfo对象
|
||||
*/
|
||||
FileInfo upload(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param bucket 文件桶名
|
||||
* @param file 文件对象
|
||||
* @return FileInfo对象
|
||||
*/
|
||||
FileInfo upload(String bucket, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param fileUri 文件的资源定位符
|
||||
* @return 文件流
|
||||
*/
|
||||
InputStream download(String fileUri);
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param bucket 文件桶名
|
||||
* @param fileUri 文件的资源定位符
|
||||
* @return 文件流
|
||||
*/
|
||||
InputStream download(String bucket, String fileUri);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param fileUri 文件的资源定位符
|
||||
* @return 删除状态
|
||||
*/
|
||||
Boolean delete(String fileUri);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param bucket 文件桶名
|
||||
* @param fileUri 文件的资源定位符
|
||||
* @return 删除状态
|
||||
*/
|
||||
Boolean delete(String bucket, String fileUri) throws IOException;
|
||||
}
|
||||
```
|
||||
|
||||
4. `FileStoreCombinationService`类可以直线多种对象存储混合使用,只需要在方法上添加上需要指定的储存类型枚举即可
|
||||
|
||||
## common-redis
|
||||
### 简介
|
||||
对redis中`RedisTemplate`进行了fastjson序列化的封装,也保留了原本的Java序列化方式
|
||||
|
||||
对fastjson序列化封装的`RedisTemplate`进行了进一步的封装实现了`RedisService`,让开发者更方便的使用
|
||||
|
||||
## common-security
|
||||
### 简介
|
||||
集群中最核心的部分,spring security 安全校验封装,自定义PermissionService实现spring security自定校验方法,实现自定义校验逻辑,使用Inner注解实现集群内rpc远程调用接口的区分
|
||||
|
||||
## common-swagger
|
||||
### 简介
|
||||
swagger接口文档配置,通过SwaggerConfig类从nacos中获取到当前服务对应的swagger配置信息,生成当前服务的swagger接口文档数据,配合gateway swagger聚合生成集群的swagger聚合文档
|
||||
|
||||
解决swagger聚合文档请求/v3/doc-api接口时,swaggerui无法发起请求的bug
|
||||
|
||||
### 使用
|
||||
1. 引入配置
|
||||
```xml
|
||||
<!--swagger文档-->
|
||||
<dependency>
|
||||
<groupId>cn.odliken</groupId>
|
||||
<artifactId>common-swagger</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. 在yml中填写好配置信息
|
||||
```yaml
|
||||
swagger:
|
||||
title: 演示api文档
|
||||
description: 演示api文档
|
||||
```
|
||||
|
||||
## common-email
|
||||
### 简介
|
||||
为系统提供最基础的邮件发送功能,其中实现同步邮件发送和异步邮件发送两种功能实现
|
||||
|
||||
### 使用
|
||||
1. 引入依赖
|
||||
```xml
|
||||
<!--邮件发送组件-->
|
||||
<dependency>
|
||||
<groupId>cn.odliken</groupId>
|
||||
<artifactId>common-email</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. 在yml中填写好配置信息
|
||||
```yaml
|
||||
email:
|
||||
sender: 发送人邮箱
|
||||
personal: 发送人名称
|
||||
email-smtp-host: 邮件服务器
|
||||
email-smtp-port: 邮件服务器端口
|
||||
username: 发送人用户名
|
||||
password: 发送人密码
|
||||
encryption: 加密协议
|
||||
```
|
||||
3. 使用
|
||||
注入`EmailService`后,使用`EmailService`中的同步发送邮件或者异步发送邮件即可进行邮件发送
|
||||
|
||||
## common-lock
|
||||
### 简介
|
||||
为系统提供分布式锁的能力,采用的是redisson实现,其中使用注解+aop+反射实现防重复提交和接口的分布式锁访问两大功能,并对提供封装好的分布式锁服务类
|
||||
|
||||
### 防重复提交
|
||||
防重复提交是指在进行数据提交的过程中,防止用户重复提交相同的数据或者重复执行同一操作的一种技术手段。这种技术手段的主要目的是避免数据的重复录入或者重复操作所带来的不必要的麻烦和风险,同时也可以提高应用程序的稳定性和可靠性。系统使用后端校验+分布式锁的方式实现
|
||||
|
||||
### 锁的唯一性
|
||||
锁的唯一性保证是通过用户登录后的token(匿名用户则使用客户端ip)+加上uri+方法请求类型+需要使用的请求参数组成的key保证锁的唯一性
|
||||
|
||||
### 使用
|
||||
1. 引入依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.odliken</groupId>
|
||||
<artifactId>common-lock</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. 在接口上添加注解
|
||||
```java
|
||||
@PostMapping
|
||||
@ResubmitLock
|
||||
public Result<String> login(@Validated @RequestBody LoginBody login) {
|
||||
return Result.ok("操作成功");
|
||||
}
|
||||
```
|
||||
|
||||
3. 拦截效果
|
||||

|
||||
|
||||
### 通用分布式锁
|
||||
通用分布式锁主要通过
|
||||
|
||||
### 分布式锁
|
||||
分布式锁的实现与防重复提交实现原理相似,也是采用注解+aop+反射实现
|
||||
|
||||
|
||||
## common-websocket
|
||||
### 简介
|
||||
该模块使用Netty搭建websocket服务,并将websocket服务注册到nacos实现Netty集群,之后可以使用Gateway实现代理和负载均衡
|
||||
|
||||
### 使用
|
||||
1. 引入依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.odliken</groupId>
|
||||
<artifactId>common-websocket</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. 配置yml文件
|
||||
```yaml
|
||||
websocket:
|
||||
application-name: netty-application-name
|
||||
port: 8080
|
||||
path: /websocket
|
||||
```
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 27 KiB |
|
|
@ -0,0 +1 @@
|
|||
# 自定义查询
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# 网关服务模块
|
||||
在集群环境中,gateway将会是集群网络的唯一出入口,是系统安全的重要节点,同时gateway承担Sentinel限流,服务分发负载均衡和提供swagger文档等工作。
|
||||
|
||||
## 文件结构
|
||||
```lua
|
||||
gateway
|
||||
|
||||
└── java
|
||||
├── config -- 配置包
|
||||
├── controller -- swagger聚合需要使用的到的接口
|
||||
├── filter -- 全局过滤器
|
||||
├── handler -- 异常处理
|
||||
├── util -- 工具
|
||||
└── GatewayApplication.java -- 启动类
|
||||
└── resources
|
||||
├── bootstrap.yml -- 配置文件
|
||||
└── logback-spring.xml -- 日志记录文件
|
||||
```
|
||||
|
||||
## 配置文件
|
||||
```yaml
|
||||
# Tomcat
|
||||
server:
|
||||
port: 5000
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: monitor
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: local
|
||||
security:
|
||||
user:
|
||||
name: root
|
||||
password: password
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.101.108:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: ${spring.profiles.active}
|
||||
metadata:
|
||||
management:
|
||||
context-path: /actuator
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
file-extension: yaml
|
||||
namespace: ${spring.profiles.active}
|
||||
shared-configs:
|
||||
- ${spring.application.name}-${spring.profiles.active}.yml
|
||||
```
|
||||
配置文件相当简洁,主要为设置运行端口,active的环境以及注册到nacos,并从nacos读取到gateway自身的配置文件
|
||||
|
||||
属于gateway的配置文件:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
discovery:
|
||||
locator:
|
||||
# gateway 能够进行服务发现
|
||||
enabled: true
|
||||
routes:
|
||||
# 认证中心
|
||||
- id: auth
|
||||
uri: lb://auth
|
||||
predicates:
|
||||
- Path=/auth/**
|
||||
```
|
||||
|
||||
## 路由分发
|
||||
路由分发是gateway的一个重要功能,当gateway启动后会向nacos发起注册,并从nacos cconfig中心获取到gateway的那一份配置文件,gateway再将文件加载到自己的路由中,当又接口请求到后,gateway则可以根据路由表对请求进行分发。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
# 网关服务模块
|
||||
在集群环境中,gateway将会是集群网络的唯一出入口,是系统安全的重要节点,同时gateway承担Sentinel限流,服务分发负载均衡和提供swagger文档等工作。
|
||||
|
||||
## 文件结构
|
||||
```lua
|
||||
gateway
|
||||
|
||||
└── java
|
||||
├── config -- 配置包
|
||||
├── controller -- swagger聚合需要使用的到的接口
|
||||
├── filter -- 全局过滤器
|
||||
├── handler -- 异常处理
|
||||
├── util -- 工具
|
||||
└── GatewayApplication.java -- 启动类
|
||||
└── resources
|
||||
├── bootstrap.yml -- 配置文件
|
||||
└── logback-spring.xml -- 日志记录文件
|
||||
```
|
||||
|
||||
## 配置文件
|
||||
```yaml
|
||||
# Tomcat
|
||||
server:
|
||||
port: 5000
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: monitor
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: local
|
||||
security:
|
||||
user:
|
||||
name: root
|
||||
password: password
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.101.108:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: ${spring.profiles.active}
|
||||
metadata:
|
||||
management:
|
||||
context-path: /actuator
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
file-extension: yaml
|
||||
namespace: ${spring.profiles.active}
|
||||
shared-configs:
|
||||
- ${spring.application.name}-${spring.profiles.active}.yml
|
||||
```
|
||||
配置文件相当简洁,主要为设置运行端口,active的环境以及注册到nacos,并从nacos读取到gateway自身的配置文件
|
||||
|
||||
属于gateway的配置文件:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
discovery:
|
||||
locator:
|
||||
# gateway 能够进行服务发现
|
||||
enabled: true
|
||||
routes:
|
||||
# 认证中心
|
||||
- id: auth
|
||||
uri: lb://auth
|
||||
predicates:
|
||||
- Path=/auth/**
|
||||
```
|
||||
|
||||
## 路由分发
|
||||
路由分发是gateway的一个重要功能,当gateway启动后会向nacos发起注册,并从nacos cconfig中心获取到gateway的那一份配置文件,gateway再将文件加载到自己的路由中,当又接口请求到后,gateway则可以根据路由表对请求进行分发。
|
||||
|
||||
gateway中配置了全局路由器,对
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# 通用消息公告模块
|
||||
## 简介
|
||||
痛用消息模块使用netty实现的websocket,引入common-websocket模块进行实现,基于netty集群+rabbitmq实现集群模式下的消息精准推送功能。
|
||||
|
||||
通用消息模块可为系统任意模块提供消息发布和websocket消息推送功能,并对外提供rpc消息发送功能。
|
||||
|
||||
消息推送时可以指定发送到指定用户,发送到指定角色,发送到指定部门或者全部发送四大模式。
|
||||
|
||||
在用户侧websocket不作为用户与service进行消息发送的桥梁,websocket只会进行消息的推送和授权,消息的发送则是使用消息发送接口进行。
|
||||
|
||||
## 消息推送时序图
|
||||

|
||||
|
||||
## 消息发送确认机制
|
||||
|
||||

|
||||
- 当用户发送消息后,消息首选会数据库进行持久化,持久化成功推送至mq,若消息推送失败则发生数据库回滚。
|
||||
- mq接受到消息之后,向netty的监听服务发起事件,netty进行消费,消费成功手动ack确认,若3次消费失败,则认为当前服务消费失败,手动ack确认消息消费失败,并删除mq中的消息。
|
||||
|
||||
## websocket连接与授权
|
||||
连接: websocket请求url为http://gateway-service/notice-ws/notice。
|
||||
|
||||
授权: 授权需要获取到用户登录时获取到的token,并按照规定数据格式在连接成功websocket之后发送授权信息,如果连接websocket 3s内未发送授权验证信息,连接将被自动断开。
|
||||
```json
|
||||
{
|
||||
"token": "用户令牌",
|
||||
"cluster": "消息群组"
|
||||
}
|
||||
```
|
||||
|
||||
## notice服务实现模块分离
|
||||
在notice服务中,消息体或者websocket连接用户授权是都需要指定到消息群组(cluster),notice服务则通过cluster区分消息群组,并对其用户的websocket连接进行隔离。
|
||||
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,15 @@
|
|||
# 通用用户权限管理模块
|
||||
## 服务简介
|
||||
通用用户权限模块为集群中的核心,集群中的系统基础服务的在此实现
|
||||
|
||||
## 提供服务
|
||||
- 用户管理
|
||||
- 角色管理
|
||||
- 菜单管理
|
||||
- 部门管理
|
||||
- 岗位管理
|
||||
- 字典管理
|
||||
- 字典类型配置
|
||||
- 在线用户管理
|
||||
- fegin rpc远程调用api服务
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# 图形化管理模块
|
||||
主要实现集群中的一些扩展功能:
|
||||
- code-gen -- 分布式可视化代码生成
|
||||
- custom-query -- 自定义查询
|
||||
- flowable -- workflow工作流模块
|
||||
- monitor -- 服务监控
|
||||
- sentinel-dashboard -- sentinel监控面板
|
||||
- sentinel-dashboard-pro -- sentinel监控面板,配置规则同步到nacos管理
|
||||
|
||||
## code-gen(正在开发)
|
||||
为提高集群编码效率,可以快速生成crud后端代码和配套的前端页面代码,以数据库元数据作为数据基础是.
|
||||
|
||||
在集群模式下每一个服务所在的数据库地址不同,数据库不同,甚至数据库类型不同,所以提供数据库数据源的配置,支持mysql,MariaDB,以及oracle三种数据库
|
||||
|
||||
支持可视化的代码生成配置,并且根据数据源不同生成对应数据库的标准curd代码
|
||||
|
||||
## custom-query(正在规划)
|
||||
将ebts中的自定义查询和er可视化查询集群化,ebts网站:https://demo.ebts.top/
|
||||
|
||||
## flowable
|
||||
### 简介
|
||||
flowable模块为系统提供工作流的服务支撑,前端采用开源的防钉钉bpmn编辑器,并对前端编辑器进行升级和自定义功能的开发
|
||||
|
||||
### 实现功能
|
||||
- 流程发布
|
||||
- 自定义表单
|
||||
- 表单节点权限控制
|
||||
- 流程流转
|
||||
- 全新的日志记录
|
||||
- 发起流程时全新的全局流程
|
||||
- 流程流转是邮件提醒
|
||||
- 自定义监听器
|
||||
- 触发器http请求js可编程影响流程流转结果
|
||||
- 流程自定义任意节点回滚
|
||||
- 表单编辑器可自定义组件开发
|
||||
|
||||
### 1. 流程发布
|
||||
流程发布为workflow第一个步骤,需要用户自定义流程,配置审批表单,并且可以对表单权限与节点权限进行控制
|
||||
|
||||
#### 审批流程
|
||||
审批流程共实现如下节点
|
||||
|
||||
- 审批人:审批节点,可以选择指定用户对本次流程进行审批
|
||||
- 抄送人:将当前流程抄送给设置的用户进行查看
|
||||
- 条件分支:可以设置对条件影响流程的执行,其中可使用表单中的参数
|
||||
- 并行分支:可以同时执行两条或多条审批路线
|
||||
- 延时等待:可以让流程在指定时间或者指定等待时间执行
|
||||
- 触发器:可以发起http请求或者邮件
|
||||
|
||||
#### 审批人
|
||||
审批人实现多种方式指定审批人,配置下图所示:
|
||||

|
||||
|
||||
当用户选择了对应的审批对象后,系统则会根据对应的审批对象去获取对应的审批人,加入到审批流程中,并且还对其审批人为和审批期限进行辅助
|
||||
|
||||
#### 抄送人
|
||||
抄送人节点只需要选择对应的抄送人即可,后续可扩展和审批人相同的选择审批对象
|
||||
|
||||
#### 条件分支
|
||||
条件分支节点下可以设置多种条件,条件可以进行多种自定义组合,实现用户指定的流程流转方向
|
||||
|
||||
#### 触发器
|
||||
触发器可以发起http请求或者email邮件,发起http请求的时候可以编写自定义脚本来处理http的响应结果,并可以影响到整个流程的流程
|
||||
|
||||
#### 流程简单demo
|
||||

|
||||
|
||||
### 自定义表单
|
||||
自定义表单实现为拖拽的方式进行表单的自定义设计,这样用户就可以制作任意业务需求的表单满足所以的业务场景
|
||||

|
||||
|
||||
### 发起流程
|
||||
流程发起提供左右两个区域,左侧区域为表单输入位置,右侧为流程的预览,此处可以看到流程执行情况以及当前流程对应的审批人
|
||||

|
||||
|
||||
### 查看流程
|
||||
到我的处理页面即可查看到需要当前登录用户处理的流,点击之后就可以看到流程的信息,此处定制开发流程的日志信息
|
||||

|
||||
|
||||
### 我发起的流程
|
||||
登录用户查看我发起的,当前页面可以查看到流程当前的节点,当前的审批人,流程状态等,点击流程就可以查看到流程的详细情况,详细情况和查看流程完全一样,并且新增全局的流程信息
|
||||

|
||||

|
||||
流程全局预览和日志记录可以记录每个操作的情况,列如那些用户没有审批,那些节点处于审批状态,那个节点被拒绝,并且拒绝的用户是谁等都可以记录下来,方便后续精准的定位人员
|
||||
|
||||
## sentinel-dashboard
|
||||
sentinel的控制面板
|
||||
|
||||
## sentinel-dashboard-pro
|
||||
实现了sentinel配置规则落地到nacos中
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
|
@ -1,50 +0,0 @@
|
|||
### 1001 害死人不偿命的(3n+1)猜想 (15分)
|
||||
|
||||
**卡拉兹(Callatz)猜想**:
|
||||
对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步得到n=1。卡拉兹在1950年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证(3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
|
||||
我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过1000的正整数n,简单地数一下,需要多少步(砍几下)才能得到n=1?
|
||||
|
||||
**输入格式**:
|
||||
|
||||
每个测试输入包含1个测试用例,即给出自然数n的值。
|
||||
|
||||
**输出格式**:
|
||||
|
||||
输出从 n 计算到1需要的步数。
|
||||
|
||||
**输入样例**:
|
||||
|
||||
~~~ bash
|
||||
3
|
||||
~~~
|
||||
|
||||
**输出样例**:
|
||||
|
||||
~~~ bash
|
||||
5
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
代码实现:
|
||||
|
||||
~~~ java
|
||||
//java代码:
|
||||
import java.util.Scanner;
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner =new Scanner(System.in);
|
||||
int N = scanner.nextInt();
|
||||
int steps =0;
|
||||
while (N !=1) {
|
||||
if (N %2 ==0) {
|
||||
N = N /2;
|
||||
} else {
|
||||
N = (3 * N +1) / 2;
|
||||
}
|
||||
steps++;
|
||||
}
|
||||
System.out.println(steps);
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# sentinel dashboard 限流模块
|
||||
> sentinel dashboard可以通过页面图形化的方式监控机器以及服务的运行情况和配置复杂的各种流控、降级等规则,以保证线上服务的稳定运行,本篇文章主要是介绍控制台参数意义和使用规则,达到快速使用的目的
|
||||
|
||||
## 具体实现
|
||||
ebts-cloud快速开发系统有两个sentinel dashboard模块,一个是官方给的完整版,另外一个为集成了nacos持久化,适合生产环境使用
|
||||
|
||||
### 官方提供
|
||||
|
||||
- xml
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- 添加yaml配置文件到client服务中
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
sentinel:
|
||||
transport:
|
||||
# sentinel dashboard服务地址
|
||||
dashboard: localhost:5000
|
||||
```
|
||||
|
||||
### nacos升级
|
||||
- 配置数据持久化到nacos
|
||||
- client注册到sentinel dashboard时,sentinel dashboard将nacos上对应的配置文件初始化给client
|
||||
- 支持配置设置针对单一client设置配置文件还是集群内全部服务设置配置
|
||||
|
||||
> 配置与上方相同,只需要添加nacos的配置中心信息即可
|
||||
|
||||

|
||||
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
|
@ -0,0 +1,113 @@
|
|||
# 监控模块使用
|
||||
|
||||
监控模块使用 Spring Boot Admin,配合客户端引入spring-boot-starter-actuator依赖就可以在监控模块中看到对应服务的情况,但是Spring Boot Admin没有权限拦截,所以需要二次权限认证。
|
||||
|
||||
## 实现原理
|
||||
- 引入 spring security
|
||||
|
||||
```xml
|
||||
<!--security-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- 配置 spring security
|
||||
```java
|
||||
/**
|
||||
* 配置安全认证,以便其他服务注册
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/11/10
|
||||
*/
|
||||
@Configuration
|
||||
public class SecuritySecureConfig {
|
||||
|
||||
/**
|
||||
* 应用上下文路径
|
||||
*/
|
||||
private final String adminContextPath;
|
||||
|
||||
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
|
||||
this.adminContextPath = adminServerProperties.getContextPath();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
successHandler.setTargetUrlParameter("redirectTo");
|
||||
successHandler.setDefaultTargetUrl(adminContextPath + "/");
|
||||
http.authorizeRequests()
|
||||
//1.配置所有静态资源和登录也可以公开访问
|
||||
.antMatchers(adminContextPath + "/assets/**")
|
||||
.permitAll()
|
||||
.antMatchers(adminContextPath + "/login")
|
||||
.permitAll()
|
||||
//2. 其他请求,必须经过认证
|
||||
.antMatchers("/actuator/**","/instances").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
//3. 配置登录和登出路径
|
||||
.formLogin().loginPage(adminContextPath + "/login")
|
||||
.successHandler(successHandler)
|
||||
.and()
|
||||
.logout().logoutUrl(adminContextPath + "/logout");
|
||||
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpHeadersProvider customHttpHeadersProvider() {
|
||||
return (instance) -> {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add(SecurityConstants.ACTUATOR_FROM, SecurityConstants.ACTUATOR_FROM_IN);
|
||||
return httpHeaders;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
- 在对应的monitor-运行环境.yml 配置用户
|
||||
```yaml
|
||||
spring:
|
||||
security:
|
||||
user:
|
||||
name: root
|
||||
password: password
|
||||
```
|
||||
|
||||
## 客户端配置
|
||||
- 在对应的application-运行环境.yml 配置actuator暴露信息
|
||||
```yaml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
# 设置是否暴露端点 默认只有health和info可见
|
||||
exposure:
|
||||
# include: env # 方式1: 暴露端点env,配置多个以,隔开
|
||||
include: "*" # 方式2: 包括所有端点,注意需要添加引号
|
||||
# 排除端点
|
||||
exclude: shutdown
|
||||
server:
|
||||
port: 9595 # 开监控端口,不和应用用同一个端口, 服务端口,在使用k8s的情况下,每一个服务都是在单独的一个docker中,所以他们的端口是不会发生冲突的
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always # 显示db、redis、rabbti连接情况等
|
||||
shutdown:
|
||||
enabled: true #默认情况下,除shutdown以外的所有端点均已启用。手动开启
|
||||
```
|
||||
|
||||
## 效果图
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
|
@ -0,0 +1,184 @@
|
|||
# Excel快速导出
|
||||
|
||||
## 功能介绍
|
||||
- 在对应实现类上加上自定义注解`@Excel`后,在编写web接口是调用ExcelUtil.exportExcel即可实现对应实体Excel的快速导出
|
||||
|
||||
## 实现原理
|
||||
- 主要使用到Java反射机制,获取到目标实体的Class对象后通过java发射获取到每个字段,并且获取到对应字段上的@Excel注解,将注解中的数据记录下来作为Excel中的表头。再通过java反射获取到对应的数据并做好与标表头的映射关系,最终使用ease-excel实现Excel的渲染
|
||||
|
||||
## 代码展示
|
||||
```java
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/19
|
||||
*/
|
||||
public class ExcelUtil {
|
||||
|
||||
public static List<ExcelAssist> assistList = new ArrayList<>();
|
||||
|
||||
public static Map<String,Map<String,DictData>> dictDataMap;
|
||||
|
||||
|
||||
private static DictDataService getBean() {
|
||||
return SpringContextHolder.getBean(DictDataService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到数据列表
|
||||
*
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
private static List<List<Object>> getDataList(List<?> list) {
|
||||
return list.stream().map(object ->
|
||||
assistList.stream().map(assist -> {
|
||||
Field field = assist.getField();
|
||||
Excel excel = assist.getExcel();
|
||||
Object value = null;
|
||||
if (StringUtils.isEmpty(assist.getObjectFieldName())) {
|
||||
value = getData(object, excel, field);
|
||||
} else {
|
||||
Object sunValue = null;
|
||||
try {
|
||||
sunValue = ReflectUtils.invokeGetter(object, assist.getObjectFieldName());
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
if (null != sunValue) {
|
||||
value = getData(object, excel, field);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}).collect(Collectors.toList())
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到对象中的数据
|
||||
*
|
||||
* @param object
|
||||
* @param excel
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
private static Object getData(Object object, Excel excel, Field field) {
|
||||
try {
|
||||
Object objectValue = ReflectUtils.invokeGetter(object, field.getName());
|
||||
if (objectValue instanceof Date) {
|
||||
objectValue = DateUtils.parseDateToStr(excel.dateFormat(), (Date) objectValue);
|
||||
}
|
||||
String dictType = excel.dictType();
|
||||
if (StringUtils.isNotEmpty(dictType)){
|
||||
Map<String,DictData> dictMap = dictDataMap.get(dictType);
|
||||
if (null != dictMap){
|
||||
DictData dictData = dictMap.get(objectValue.toString());
|
||||
if (null!= dictData){
|
||||
objectValue = dictData.getDictLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
return objectValue;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化header数据
|
||||
*
|
||||
* @param clazz
|
||||
* @param assist
|
||||
*/
|
||||
private static void initHeader(Class<?> clazz, ExcelAssist assist) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
Excel excel = field.getAnnotation(Excel.class);
|
||||
if (null != excel) {
|
||||
assistList.add(new ExcelAssist(field, excel, assist));
|
||||
} else {
|
||||
Excels excels = field.getAnnotation(Excels.class);
|
||||
if (excels == null) {
|
||||
continue;
|
||||
}
|
||||
Class<?> objectValue = field.getType();
|
||||
if (clazz == objectValue) {
|
||||
throw new CustomException("不允许嵌套对象导出Excel");
|
||||
}
|
||||
ExcelAssist excelAssist = new ExcelAssist(field, excels);
|
||||
initHeader(objectValue, excelAssist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportExcel(List<?> list, Class<?> clazz) {
|
||||
exportExcel(list, clazz, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据Excel
|
||||
*
|
||||
* @param list
|
||||
* @param clazz
|
||||
* @param sheetName
|
||||
*/
|
||||
public static void exportExcel(List<?> list, Class<?> clazz, String sheetName) {
|
||||
Set<String> dictList = new HashSet<>();
|
||||
|
||||
initHeader(clazz, null);
|
||||
List<List<String>> headerList = new ArrayList<>(assistList.size());
|
||||
assistList = assistList.stream().sorted(Comparator.comparing(ExcelAssist::getOrder))
|
||||
.peek(assist -> headerList.add(Collections.singletonList(assist.getExcel().value())))
|
||||
.peek(assist -> {
|
||||
String dictType = assist.getExcel().dictType();
|
||||
if (StringUtils.isNotEmpty(dictType)) {
|
||||
dictList.add(dictType);
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
if (dictList.size() > 0) {
|
||||
getDictData(new ArrayList<>(dictList));
|
||||
}
|
||||
List<List<Object>> dataList = getDataList(list);
|
||||
try {
|
||||
sheetName = StringUtils.isEmpty(sheetName) ? "sheet" : sheetName;
|
||||
HttpServletResponse response = getResponse(sheetName);
|
||||
EasyExcel.write(response.getOutputStream())
|
||||
.head(headerList)
|
||||
.excelType(ExcelTypeEnum.XLSX)
|
||||
.sheet(StringUtils.isEmpty(sheetName) ? "sheet" : sheetName)
|
||||
.doWrite(dataList);
|
||||
} catch (IOException e) {
|
||||
throw new CustomException("Excel导出失败!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static HttpServletResponse getResponse(String sheetName){
|
||||
HttpServletResponse response = SecurityUtils.getResponse();
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setContentType("multipart/form-data");
|
||||
response.setHeader("Content-Disposition" ,
|
||||
"attachment;fileName=" + sheetName + UUID.randomUUID() + ".xlsx");
|
||||
return response;
|
||||
}
|
||||
|
||||
private static void getDictData(List<String> dictList) {
|
||||
DictDataService dictDataService = getBean();
|
||||
Result<Map<String, Map<String,DictData>>> result = dictDataService.searchDictDataCacheKeys(dictList, SecurityConstants.FROM_IN);
|
||||
System.out.println(result);
|
||||
if (result.getCode() == ResultConstant.SUCCESS_CODE){
|
||||
dictDataMap = result.getData();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
## 使用demo
|
||||
```java
|
||||
@ApiOperation("导出excel数据")
|
||||
@GetMapping("/export")
|
||||
@PreAuthorize("@ss.hasPermission('code:goods:export')")
|
||||
public void export(DemoGoodsQuery query){
|
||||
List<DemoGoodsVo> list = demoGoodsService.exportDemoGoodsList(query);
|
||||
ExcelUtil.exportExcel(list,DemoGoodsVo.class);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
# 树形结构转换工具类
|
||||
|
||||
## 功能介绍
|
||||
- 将 A类的List数组快速转换为 B类型 List 的树形结构列表
|
||||
|
||||
## 实现原理
|
||||
- 主要使用到Java反射机制,将A类中的字段拷贝到B类中,并且根据A类中id 与 pid之间的关系快速构建树形结构
|
||||
- 设置配置类,更方便的实现A类与B类之前转换的需要,配置类包含有id字段与pid字段之间自定义设置,A类中需要转换到B类中的字段,以及是需要Spring 中 BeanUtils直接copy,以及如果使用了BeanUtil后需要排除的字段
|
||||
|
||||
## 代码展示
|
||||
```java
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/16
|
||||
*/
|
||||
public class TreeUtil {
|
||||
|
||||
private static TreeConfig config;
|
||||
|
||||
private static Class<?> targetClass;
|
||||
|
||||
/**
|
||||
* 构建tree结构
|
||||
*
|
||||
* @param list
|
||||
* @param target
|
||||
* @param node
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> build(List<?> list, Class<T> target, Node node) {
|
||||
//初始化config
|
||||
config = new TreeConfig();
|
||||
//将目标class对象设置为全局对象
|
||||
TreeUtil.targetClass = target;
|
||||
//提供给实现类对config进行修改
|
||||
node.build(config);
|
||||
//获取到最小的
|
||||
Object min = list.stream().min(Comparator.comparing(object -> getParentId(object).toString())).get();
|
||||
//获取到最小的父级id
|
||||
Object minPid = getParentId(min);
|
||||
//将数据通过他们的各自的父id进行分组
|
||||
Map<Object, List<Object>> listMap = list.stream().collect(Collectors.groupingBy(object -> getParentId(object)));
|
||||
//最终开始进行tree的构建
|
||||
return getChildren(listMap, minPid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到子节点
|
||||
*
|
||||
* @param listMap
|
||||
* @param parentId
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
private static <T> List<T> getChildren(Map<Object, List<Object>> listMap, Object parentId) {
|
||||
//获取到缓存中的list
|
||||
List<?> objects = listMap.get(parentId);
|
||||
if (chickList(objects)) {
|
||||
return null;
|
||||
}
|
||||
listMap.remove(parentId);
|
||||
//遍历数组,并将对象一一的转换
|
||||
List<Object> collect = objects.stream().map(object ->
|
||||
conversion(object, listMap)).collect(Collectors.toList());
|
||||
return (List<T>) collect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象直接的字段进行转换
|
||||
*
|
||||
* @param object
|
||||
* @param listMap
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
private static <T> T conversion(Object object, Map<Object, List<Object>> listMap) {
|
||||
try {
|
||||
Object targetObject = targetClass.newInstance();
|
||||
//相同字段名称直接赋值
|
||||
if (config.isCopy()){
|
||||
BeanUtils.copyProperties(object, targetObject);
|
||||
}
|
||||
//不同字段名进行赋值
|
||||
for (Map.Entry<String, String> entry : config.getMapper().entrySet()) {
|
||||
String targetKey = entry.getKey();
|
||||
String sourceKey = entry.getValue();
|
||||
Object value = ReflectUtils.invokeGetter(object, sourceKey);
|
||||
Object[] args = new Object[]{value};
|
||||
ReflectUtils.invokeSetter(targetObject, targetKey, args);
|
||||
}
|
||||
//设置子节点
|
||||
Object id = ReflectUtils.invokeGetter(object, config.getIdField());
|
||||
List<Object> children = getChildren(listMap, id);
|
||||
if (!chickList(children)) {
|
||||
ReflectUtils.invokeSetter(targetObject, config.getChildrenField(), new Object[]{children});
|
||||
}
|
||||
//设置完毕后,将需要排除的字段进行置空
|
||||
for (String key : config.getExclude()) {
|
||||
ReflectUtils.invokeSetNull(targetObject, key);
|
||||
}
|
||||
//返回结果
|
||||
return (T) targetObject;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查list是否为空
|
||||
*
|
||||
* @param objects
|
||||
* @return
|
||||
*/
|
||||
private static boolean chickList(List<?> objects) {
|
||||
return null == objects || objects.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到父级id
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
private static Object getParentId(Object object) {
|
||||
try {
|
||||
return ReflectUtils.invokeGetter(object, config.getParentField());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
## 使用demo
|
||||
```java
|
||||
@Override
|
||||
public List<DeptVo> selectDeptTree(String deptName, Integer state) {
|
||||
List<Dept> deptList = deptMapper.selectDeptList(deptName, state);
|
||||
return TreeUtil.build(deptList,DeptVo.class,(config)->{
|
||||
config.setIdField("deptId");
|
||||
config.setMapper("key","deptId");
|
||||
config.setExclude("phone");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
# 后端部署
|
||||
## 需要部署的内容
|
||||
- auth
|
||||
- gateway
|
||||
- umps
|
||||
- auth 权限认证模块
|
||||
- gateway 网关服务模块
|
||||
- umps 系统用户权限管理模块
|
||||
|
|
|
|||
|
|
@ -1,50 +1,36 @@
|
|||
# 介绍
|
||||
## 它是如何工作的?
|
||||
**卡拉兹(Callatz)猜想**:
|
||||
对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步得到n=1。卡拉兹在1950年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证(3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……
|
||||
我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过1000的正整数n,简单地数一下,需要多少步(砍几下)才能得到n=1?
|
||||
# 项目简介
|
||||
|
||||
**输入格式**:
|
||||
## 项目背景
|
||||
互联网对高并发集群的需求愈发的变大,spring boot 的单体应用已经不满足于现在复杂的业务需求,spring cloud逐渐成为高并发集群的标杆,学习spring cloud也刻不容缓,
|
||||
该项目是在学习了spring cloud中各种组件之后,为锻炼自己的集群能力,并且自己也想做一套集群的基础权限管理系统。
|
||||
|
||||
每个测试输入包含1个测试用例,即给出自然数n的值。
|
||||
该集群为一套基础的权限管理系统,主导权限控制,权限管理等核心功能,并且配合配合visual(图形化管理模块)对集群的资源进行更方便的关系,提高软件开发的效率。
|
||||
|
||||
**输出格式**:
|
||||
## 系统说明
|
||||
- 基于 Spring Cloud 2021 、Spring Boot 2.7、 Spring Security 的权限管理系统
|
||||
- 采用前后端分离的模式,前端(基于 Security-React, Security-Vue)
|
||||
- 注册中心,配置中心使用Nacos,
|
||||
- 权限认证使用Spring Security + Redis
|
||||
- 流量控制使用Sentinel
|
||||
- gitea+drone+harbor+rancher全套部署流程
|
||||
|
||||
输出从 n 计算到1需要的步数。
|
||||
## 核心依赖
|
||||
|
||||
**输入样例**:
|
||||
| 依赖 | 版本 |
|
||||
|----------------------|------------|
|
||||
| Spring Boot | 2.7.5 |
|
||||
| Spring Cloud | 2021.0.5 |
|
||||
| Spring Cloud Alibaba | 2021.0.4.0 |
|
||||
| Mybatis | 3.5.2 |
|
||||
| Vue | 3.1.3 |
|
||||
| React | 3.1.3 |
|
||||
|
||||
~~~ bash
|
||||
3
|
||||
~~~
|
||||
## 软件相关架构
|
||||

|
||||
|
||||
**输出样例**:
|
||||
- 集群通过gateway作为集群的唯一入口,然后访问到授权中心等其他服务,服务之间授权则使用`@inner`注解加上aop标识
|
||||
|
||||
~~~ bash
|
||||
5
|
||||
~~~
|
||||
## CI/CD
|
||||

|
||||
|
||||
|
||||
|
||||
代码实现:
|
||||
|
||||
~~~ java
|
||||
//java代码:
|
||||
import java.util.Scanner;
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner =new Scanner(System.in);
|
||||
int N = scanner.nextInt();
|
||||
int steps =0;
|
||||
while (N !=1) {
|
||||
if (N %2 ==0) {
|
||||
N = N /2;
|
||||
} else {
|
||||
N = (3 * N +1) / 2;
|
||||
}
|
||||
steps++;
|
||||
}
|
||||
System.out.println(steps);
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 89 KiB |