feat: 新增sandbox-full支持

This commit is contained in:
FamousMai
2025-03-28 15:18:33 +08:00
parent ca19bd31d4
commit b5aa970766
869 changed files with 99420 additions and 6830 deletions
+128 -172
View File
@@ -1,193 +1,149 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
# Dify-Plus
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a>
</p>
## 项目介绍
<p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> ·
<a href="https://docs.dify.ai">Documentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Enterprise inquiry</a>
</p>
在原有 Dify 的基础中,该项目做了一些二开以及新增了管理中心的功能,原先这些功能只是在我们企业内部使用,对外交流后发现很多伙伴也遇到我们相同一些痛点,故将我们的二开内容进行开源,欢迎大家一起交流。
<p align="center">
<a href="https://dify.ai" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/Product-F04438"></a>
<a href="https://dify.ai/pricing" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/free-pricing?logo=free&color=%20%23155EEF&label=pricing&labelColor=%20%23528bff"></a>
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
<img alt="Commits last month" src="https://img.shields.io/github/commit-activity/m/langgenius/dify?labelColor=%20%2332b583&color=%20%2312b76a"></a>
<a href="https://github.com/langgenius/dify/" target="_blank">
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alanggenius%2Fdify%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
<a href="https://github.com/langgenius/dify/discussions/" target="_blank">
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langgenius/dify?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
</p>
简而言之:该项目基于 [gin-vue-admin](https://github.com/flipped-aurora/gin-vue-admin) 做了 Dify 的管理中心,基于 [Dify](https://github.com/langgenius/dify) 做了一些适合企业场景的二开功能。
<p align="center">
<a href="./README.md"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
<a href="./README_CN.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
<a href="./README_JA.md"><img alt="日本語のREADME" src="https://img.shields.io/badge/日本語-d9d9d9"></a>
<a href="./README_ES.md"><img alt="README en Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
<a href="./README_FR.md"><img alt="README en Français" src="https://img.shields.io/badge/Français-d9d9d9"></a>
<a href="./README_KL.md"><img alt="README tlhIngan Hol" src="https://img.shields.io/badge/Klingon-d9d9d9"></a>
<a href="./README_KR.md"><img alt="README in Korean" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
<a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
</p>
*Dify-Plus* = *管理中心* + *Dify 二开*
## 名字说明
Dify-Plus,该名字不是说比 Dify 项目牛的意思,意思是想说比 Dify 多做了一些针对企业场景多了一些二开的功能而已。
## 新增功能介绍
### 一. Dify 二开功能
1. 新增:用户额度
1. 对话余额限制判断
2. 异步计算用户额度逻辑
3. 左上角新增使用额度显示
4. 新增个人监测页
2. 新增:密钥额度设置
1. 新增应用 API 调用余额限制判断
3. 新增 Web 公开页登录鉴权
4. 新增:管理员同步应用到应用模版
5. 新增:后台创建用户,自动邀请进管理员空间
6. 新增:可以鉴权的 cookie
7. 新增:同步应用到模版中心
8. 新增:应用中心页面
9. 调整 :默认跳转到应用中心
10. 新增:应用使用次数记录、应用中心按照使用次数排序
11. 权限调整
1. 调整:不允许普通成员关闭模型
2. 调整:空间普通成员不渲染“模型供应商”标签
3. 调整:非管理员,隐藏密钥显示
4. 优化: csv 编码监测,修复批量请求,windows 下载后保存再上传问题
5. 优化: markdown 图片放大问题优化
## 二. 管理中心
> 代码所在目录:/admin
1. JWT 与 Dify 打通
2. 用户同步
3. 用户额度修改
4. 费用报表
## 部分功能页面展示截图
1. 应用中心
![应用中心.jpg](images/dify-plus/应用中心.jpg)
1. API密钥列表
![API密钥列表.png](images/dify-plus/API密钥列表.png)
1. 创建API密钥
![创建API密钥.jpg](images/dify-plus/创建API密钥.jpg)
1. 用户额度显示
![用户额度显示.jpg](images/dify-plus/用户额度显示.jpg)
1. 同步至应用模版
![同步至应用模版.jpg](images/dify-plus/同步至应用模版.jpg)
1. API调用测试
![API调用测试.jpg](images/dify-plus/API调用测试.jpg)
1. 个人额度修改
![个人额度修改.jpg](images/dify-plus/个人额度修改.jpg)
1. 费用报表
![费用报表.jpg.png](images/dify-plus/费用报表.png)
1. 密钥使用分析
![密钥使用分析.jpg](images/dify-plus/密钥使用分析.jpg)
1. 每月密钥额度花费
![每月密钥额度花费.jpg](images/dify-plus/每月密钥额度花费.jpg)
Dify is an open-source LLM app development platform. Its intuitive interface combines agentic AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
## 版本更新说明
## Quick start
> Before installing Dify, make sure your machine meets the following minimum system requirements:
>
>- CPU >= 2 Core
>- RAM >= 4 GiB
1. 会持续跟随 gin-vue-admin 和 Dify 两个开源项目的版本。
2. 为了标志二开的部分,我们特意在注释、文件名、方法名、表名都加上`extend`,可通过搜索这个关键字,查看我们二开的代码
</br>
## 整体服务
The easiest way to start the Dify server is through [docker compose](docker/docker-compose.yaml). Before running Dify with the following commands, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
![dify-plus.png](images/dify_plus.png)
```bash
cd dify
cd docker
cp .env.example .env
docker compose up -d
## 启动方式(docker-compose
见文档:[部署详细步骤(dockercompose](https://github.com/YFGaia/dify-plus/wiki/%E9%83%A8%E7%BD%B2%E8%AF%A6%E7%BB%86%E6%AD%A5%E9%AA%A4%EF%BC%88docker%E2%80%90compose%EF%BC%89)
## 启动方式(源码)
见文档:[部署详细步骤(源码)](https://github.com/YFGaia/dify-plus/wiki/%E9%83%A8%E7%BD%B2%E8%AF%A6%E7%BB%86%E6%AD%A5%E9%AA%A4%EF%BC%88%E6%BA%90%E7%A0%81%E9%83%A8%E7%BD%B2%EF%BC%89)
## 用户问的较多的问题
见文档:[Q&A](https://github.com/YFGaia/dify-plus/wiki/Q&A)
## 相关配置说明
- Dify 相关配置说明:https://docs.dify.ai/zh-hans/getting-started/install-self-hosted/environments
- 管理中心 相关配置说明:
- 后端:https://gin-vue-admin.com/guide/server/config.html
- 前端:https://gin-vue-admin.com/guide/web/env.html
- Dify-plus 新增环境变量说明
```
待补充
```
## 联系我们
### email
- toxingwang@gmail.com
- 906631095@qq.com
### 微信交流群
1. 防止广告进群,添加微信,输入以下代码执行结果
```python
encoded_str = "5Yqg5YWlZGlmeS1wbHVz5Lqk5rWB576kMgo="
decoded_bytes = base64.b64decode(encoded_str)
decoded_str = decoded_bytes.decode('utf-8')
print(decoded_str)
```
2. 微信二维码:
<img width="200" alt="image" src="https://github.com/user-attachments/assets/b5aa106e-4bd9-40d7-925f-05c3f5265ef6" />
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization process.
#### Seeking help
Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) if you encounter problems setting up Dify. Reach out to [the community and us](#community--contact) if you are still having issues.
> If you'd like to contribute to Dify or do additional development, refer to our [guide to deploying from source code](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
## Key features
**1. Workflow**:
Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
### 请作者喝咖啡~
<img width="200" alt="image" src="https://github.com/user-attachments/assets/9a1ce3d4-3101-46eb-8a72-0a39db5b836b" />
**2. Comprehensive model support**:
Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers).
### Star History
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
[![Star History Chart](https://api.star-history.com/svg?repos=YFGaia/dify-plus&type=Date)](https://star-history.com/#YFGaia/dify-plus&Date)
**3. Prompt IDE**:
Intuitive interface for crafting prompts, comparing model performance, and adding additional features such as text-to-speech to a chat-based app.
**4. RAG Pipeline**:
Extensive RAG capabilities that cover everything from document ingestion to retrieval, with out-of-box support for text extraction from PDFs, PPTs, and other common document formats.
**5. Agent capabilities**:
You can define agents based on LLM Function Calling or ReAct, and add pre-built or custom tools for the agent. Dify provides 50+ built-in tools for AI agents, such as Google Search, DALL·E, Stable Diffusion and WolframAlpha.
**6. LLMOps**:
Monitor and analyze application logs and performance over time. You could continuously improve prompts, datasets, and models based on production data and annotations.
**7. Backend-as-a-Service**:
All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic.
## Using Dify
- **Cloud </br>**
We host a [Dify Cloud](https://dify.ai) service for anyone to try with zero setup. It provides all the capabilities of the self-deployed version, and includes 200 free GPT-4 calls in the sandbox plan.
- **Self-hosting Dify Community Edition</br>**
Quickly get Dify running in your environment with this [starter guide](#quick-start).
Use our [documentation](https://docs.dify.ai) for further references and more in-depth instructions.
- **Dify for enterprise / organizations</br>**
We provide additional enterprise-centric features. [Log your questions for us through this chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) or [send us an email](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) to discuss enterprise needs. </br>
> For startups and small businesses using AWS, check out [Dify Premium on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one-click. It's an affordable AMI offering with the option to create apps with custom logo and branding.
## Staying ahead
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
## Advanced Setup
If you need to customize the configuration, please refer to the comments in our [.env.example](docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
If you'd like to configure a highly-available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
#### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Using AWS CDK for Deployment
Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
> We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
## Community & contact
* [Github Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
* [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
* [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
* [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
**Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
## Security disclosure
To protect your privacy, please avoid posting security issues on GitHub. Instead, send your questions to security@dify.ai and we will provide you with a more detailed answer.
## License
This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions.
版权说明:本项目在 Dify 项目基础上进行二开,需要遵守 Dify 的开源协议,如下
This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions.
+192
View File
@@ -0,0 +1,192 @@
![cover-v5-optimized](https://github.com/langgenius/dify/assets/13230914/f9e19af5-61ba-4119-b926-d10c4c06ebab)
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a>
</p>
<p align="center">
<a href="https://cloud.dify.ai">Dify Cloud</a> ·
<a href="https://docs.dify.ai/getting-started/install-self-hosted">Self-hosting</a> ·
<a href="https://docs.dify.ai">Documentation</a> ·
<a href="https://udify.app/chat/22L1zSxg6yW1cWQg">Enterprise inquiry</a>
</p>
<p align="center">
<a href="https://dify.ai" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/Product-F04438"></a>
<a href="https://dify.ai/pricing" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/free-pricing?logo=free&color=%20%23155EEF&label=pricing&labelColor=%20%23528bff"></a>
<a href="https://discord.gg/FngNHpbcY7" target="_blank">
<img src="https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a>
<a href="https://reddit.com/r/difyai" target="_blank">
<img src="https://img.shields.io/reddit/subreddit-subscribers/difyai?style=plastic&logo=reddit&label=r%2Fdifyai&labelColor=white"
alt="join Reddit"></a>
<a href="https://twitter.com/intent/follow?screen_name=dify_ai" target="_blank">
<img src="https://img.shields.io/twitter/follow/dify_ai?logo=X&color=%20%23f5f5f5"
alt="follow on X(Twitter)"></a>
<a href="https://www.linkedin.com/company/langgenius/" target="_blank">
<img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff"
alt="follow on LinkedIn"></a>
<a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web?labelColor=%20%23FDB062&color=%20%23f79009"></a>
<a href="https://github.com/langgenius/dify/graphs/commit-activity" target="_blank">
<img alt="Commits last month" src="https://img.shields.io/github/commit-activity/m/langgenius/dify?labelColor=%20%2332b583&color=%20%2312b76a"></a>
<a href="https://github.com/langgenius/dify/" target="_blank">
<img alt="Issues closed" src="https://img.shields.io/github/issues-search?query=repo%3Alanggenius%2Fdify%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98"></a>
<a href="https://github.com/langgenius/dify/discussions/" target="_blank">
<img alt="Discussion posts" src="https://img.shields.io/github/discussions/langgenius/dify?labelColor=%20%239b8afb&color=%20%237a5af8"></a>
</p>
<p align="center">
<a href="./README.md"><img alt="README in English" src="https://img.shields.io/badge/English-d9d9d9"></a>
<a href="./README_CN.md"><img alt="简体中文版自述文件" src="https://img.shields.io/badge/简体中文-d9d9d9"></a>
<a href="./README_JA.md"><img alt="日本語のREADME" src="https://img.shields.io/badge/日本語-d9d9d9"></a>
<a href="./README_ES.md"><img alt="README en Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
<a href="./README_FR.md"><img alt="README en Français" src="https://img.shields.io/badge/Français-d9d9d9"></a>
<a href="./README_KL.md"><img alt="README tlhIngan Hol" src="https://img.shields.io/badge/Klingon-d9d9d9"></a>
<a href="./README_KR.md"><img alt="README in Korean" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
<a href="./README_AR.md"><img alt="README بالعربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="./README_TR.md"><img alt="Türkçe README" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="./README_VI.md"><img alt="README Tiếng Việt" src="https://img.shields.io/badge/Ti%E1%BA%BFng%20Vi%E1%BB%87t-d9d9d9"></a>
</p>
Dify is an open-source LLM app development platform. Its intuitive interface combines agentic AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
## Quick start
> Before installing Dify, make sure your machine meets the following minimum system requirements:
>
>- CPU >= 2 Core
>- RAM >= 4 GiB
</br>
The easiest way to start the Dify server is through [docker compose](docker/docker-compose.yaml). Before running Dify with the following commands, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
```bash
cd dify
cd docker
cp .env.example .env
docker compose up -d
```
After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization process.
#### Seeking help
Please refer to our [FAQ](https://docs.dify.ai/getting-started/install-self-hosted/faqs) if you encounter problems setting up Dify. Reach out to [the community and us](#community--contact) if you are still having issues.
> If you'd like to contribute to Dify or do additional development, refer to our [guide to deploying from source code](https://docs.dify.ai/getting-started/install-self-hosted/local-source-code)
## Key features
**1. Workflow**:
Build and test powerful AI workflows on a visual canvas, leveraging all the following features and beyond.
https://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa
**2. Comprehensive model support**:
Seamless integration with hundreds of proprietary / open-source LLMs from dozens of inference providers and self-hosted solutions, covering GPT, Mistral, Llama3, and any OpenAI API-compatible models. A full list of supported model providers can be found [here](https://docs.dify.ai/getting-started/readme/model-providers).
![providers-v5](https://github.com/langgenius/dify/assets/13230914/5a17bdbe-097a-4100-8363-40255b70f6e3)
**3. Prompt IDE**:
Intuitive interface for crafting prompts, comparing model performance, and adding additional features such as text-to-speech to a chat-based app.
**4. RAG Pipeline**:
Extensive RAG capabilities that cover everything from document ingestion to retrieval, with out-of-box support for text extraction from PDFs, PPTs, and other common document formats.
**5. Agent capabilities**:
You can define agents based on LLM Function Calling or ReAct, and add pre-built or custom tools for the agent. Dify provides 50+ built-in tools for AI agents, such as Google Search, DALL·E, Stable Diffusion and WolframAlpha.
**6. LLMOps**:
Monitor and analyze application logs and performance over time. You could continuously improve prompts, datasets, and models based on production data and annotations.
**7. Backend-as-a-Service**:
All of Dify's offerings come with corresponding APIs, so you could effortlessly integrate Dify into your own business logic.
## Using Dify
- **Cloud </br>**
We host a [Dify Cloud](https://dify.ai) service for anyone to try with zero setup. It provides all the capabilities of the self-deployed version, and includes 200 free GPT-4 calls in the sandbox plan.
- **Self-hosting Dify Community Edition</br>**
Quickly get Dify running in your environment with this [starter guide](#quick-start).
Use our [documentation](https://docs.dify.ai) for further references and more in-depth instructions.
- **Dify for enterprise / organizations</br>**
We provide additional enterprise-centric features. [Log your questions for us through this chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) or [send us an email](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) to discuss enterprise needs. </br>
> For startups and small businesses using AWS, check out [Dify Premium on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one-click. It's an affordable AMI offering with the option to create apps with custom logo and branding.
## Staying ahead
Star Dify on GitHub and be instantly notified of new releases.
![star-us](https://github.com/langgenius/dify/assets/13230914/b823edc1-6388-4e25-ad45-2f6b187adbb4)
## Advanced Setup
If you need to customize the configuration, please refer to the comments in our [.env.example](docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
If you'd like to configure a highly-available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
- [Helm Chart by @LeoQuote](https://github.com/douban/charts/tree/master/charts/dify)
- [Helm Chart by @BorisPolonsky](https://github.com/BorisPolonsky/dify-helm)
- [YAML file by @Winson-030](https://github.com/Winson-030/dify-kubernetes)
#### Using Terraform for Deployment
Deploy Dify to Cloud Platform with a single click using [terraform](https://www.terraform.io/)
##### Azure Global
- [Azure Terraform by @nikawang](https://github.com/nikawang/dify-azure-terraform)
##### Google Cloud
- [Google Cloud Terraform by @sotazum](https://github.com/DeNA/dify-google-cloud-terraform)
#### Using AWS CDK for Deployment
Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/)
##### AWS
- [AWS CDK by @KevinZhao](https://github.com/aws-samples/solution-for-deploying-dify-on-aws)
## Contributing
For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
At the same time, please consider supporting Dify by sharing it on social media and at events and conferences.
> We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c).
## Community & contact
* [Github Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
* [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
* [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
* [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
**Contributors**
<a href="https://github.com/langgenius/dify/graphs/contributors">
<img src="https://contrib.rocks/image?repo=langgenius/dify" />
</a>
## Star history
[![Star History Chart](https://api.star-history.com/svg?repos=langgenius/dify&type=Date)](https://star-history.com/#langgenius/dify&Date)
## Security disclosure
To protect your privacy, please avoid posting security issues on GitHub. Instead, send your questions to security@dify.ai and we will provide you with a more detailed answer.
## License
This repository is available under the [Dify Open Source License](LICENSE), which is essentially Apache 2.0 with a few additional restrictions.
+2
View File
@@ -0,0 +1,2 @@
*.sql linguist-language=GO
*.html linguist-language=GO
+35
View File
@@ -0,0 +1,35 @@
.idea/
/web/node_modules
/web/dist
.DS_Store
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
rm_file/
/server/log/
/server/gva
/server/server
/server/latest_log
/server/__debug_bin*
server/uploads/
*.iml
web/.pnpm-debug.log
web/pnpm-lock.yaml
+76
View File
@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 303176530@qq.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+19
View File
@@ -0,0 +1,19 @@
### Contributing Guide
#### 1 Issue Guidelines
- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. If any questions come up when you are using Element, please hit [Gitter](https://gitter.im/element-en/Lobby) for help.
- Before submitting an issue, please check if similar problems have already been issued.
#### 2 Pull Request Guidelines
- Fork this repository to your own account. Do not create branches here.
- Commit info should be formatted as `[File Name]: Info about commit.` (e.g. `README.md: Fix xxx bug`)
- <font color=red>Make sure PRs are created to `develop` branch instead of `master` branch.</font>
- If your PR fixes a bug, please provide a description about the related bug.
- Merging a PR takes two maintainers: one approves the changes after reviewing, and then the other reviews and merges.
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 北京翻转极光科技有限责任公司
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+78
View File
@@ -0,0 +1,78 @@
SHELL = /bin/bash
#SCRIPT_DIR = $(shell pwd)/etc/script
#请选择golang版本
BUILD_IMAGE_SERVER = golang:1.22
#请选择node版本
BUILD_IMAGE_WEB = node:20
#项目名称
PROJECT_NAME = github.com/flipped-aurora/gin-vue-admin/server
#配置文件目录
CONFIG_FILE = config.yaml
#镜像仓库命名空间
IMAGE_NAME = gva
#镜像地址
REPOSITORY = registry.cn-hangzhou.aliyuncs.com/${IMAGE_NAME}
ifeq ($(TAGS_OPT),)
TAGS_OPT = latest
else
endif
ifeq ($(PLUGIN),)
PLUGIN = email
else
endif
#容器环境前后端共同打包
build: build-web build-server
docker run --name build-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_SERVER} make build-local
#容器环境打包前端
build-web:
docker run --name build-web-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_WEB} make build-web-local
#容器环境打包后端
build-server:
docker run --name build-server-local --rm -v $(shell pwd):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${BUILD_IMAGE_SERVER} make build-server-local
#构建web镜像
build-image-web:
@cd web/ && docker build -t ${REPOSITORY}/web:${TAGS_OPT} .
#构建server镜像
build-image-server:
@cd server/ && docker build -t ${REPOSITORY}/server:${TAGS_OPT} .
#本地环境打包前后端
build-local:
if [ -d "build" ];then rm -rf build; else echo "OK!"; fi \
&& if [ -f "/.dockerenv" ];then echo "OK!"; else make build-web-local && make build-server-local; fi \
&& mkdir build && cp -r web/dist build/ && cp server/server build/ && cp -r server/resource build/resource
#本地环境打包前端
build-web-local:
@cd web/ && if [ -d "dist" ];then rm -rf dist; else echo "OK!"; fi \
&& yarn config set registry http://mirrors.cloud.tencent.com/npm/ && yarn install && yarn build
#本地环境打包后端
build-server-local:
@cd server/ && if [ -f "server" ];then rm -rf server; else echo "OK!"; fi \
&& go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
&& go env -w CGO_ENABLED=0 && go env && go mod tidy \
&& go build -ldflags "-B 0x$(shell head -c20 /dev/urandom|od -An -tx1|tr -d ' \n') -X main.Version=${TAGS_OPT}" -v
#打包前后端二合一镜像
image: build
docker build -t ${REPOSITORY}/gin-vue-admin:${TAGS_OPT} -f deploy/docker/Dockerfile .
#尝鲜版
images: build build-image-web build-image-server
docker build -t ${REPOSITORY}/all:${TAGS_OPT} -f deploy/docker/Dockerfile .
#插件快捷打包: make plugin PLUGIN="这里是插件文件夹名称,默认为email"
plugin:
if [ -d ".plugin" ];then rm -rf .plugin ; else echo "OK!"; fi && mkdir -p .plugin/${PLUGIN}/{server/plugin,web/plugin} \
&& if [ -d "server/plugin/${PLUGIN}" ];then cp -r server/plugin/${PLUGIN} .plugin/${PLUGIN}/server/plugin/ ; else echo "OK!"; fi \
&& if [ -d "web/src/plugin/${PLUGIN}" ];then cp -r web/src/plugin/${PLUGIN} .plugin/${PLUGIN}/web/plugin/ ; else echo "OK!"; fi \
&& cd .plugin && zip -r ${PLUGIN}.zip ${PLUGIN} && mv ${PLUGIN}.zip ../ && cd ..
@@ -0,0 +1,99 @@
version: "3"
# 声明一个名为network的networks,subnet为network的子网地址,默认网关是177.7.0.1
networks:
network:
ipam:
driver: default
config:
- subnet: '177.7.0.0/16'
# 设置mysqlredis持久化保存
volumes:
mysql:
redis:
services:
web:
image: node:20
container_name: gva-web
hostname: gva-web #可以通过容器名访问
restart: always
ports:
- '8080:8080'
depends_on:
- server
working_dir: /web # 如果docker 设置了workdir 则此处不需要设置
#若网络不太好,请自行换源,如下
#command: bash -c "yarn config set registry https://registry.npmmirror.com --global && yarn install && yarn serve"
command: bash -c "yarn install && yarn serve"
volumes:
- ../../web:/web
networks:
network:
ipv4_address: 177.7.0.11
server:
image: golang:1.22
container_name: gva-server
hostname: gva-server
restart: always
ports:
- '8888:8888'
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ../../server:/server
working_dir: /server # 如果docker 设置了workdir 则此处不需要设置
command: bash -c "go env -w GOPROXY=https://goproxy.cn,direct && go mod tidy && go run main.go"
links:
- mysql
- redis
networks:
network:
ipv4_address: 177.7.0.12
mysql:
image: mysql:8.0.21 # 如果您是 arm64 架构:如 MacOS 的 M1,请修改镜像为 image: mysql/mysql-server:8.0.21
container_name: gva-mysql
hostname: gva-mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #设置utf8字符集
restart: always
ports:
- "13306:3306" # host物理直接映射端口为13306
environment:
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- mysql:/var/lib/mysql
networks:
network:
ipv4_address: 177.7.0.13
redis:
image: redis:6.0.6
container_name: gva-redis # 容器名
hostname: gva-redis
restart: always
ports:
- '16379:6379'
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- redis:/data
networks:
network:
ipv4_address: 177.7.0.14
@@ -0,0 +1,90 @@
version: "3"
# 声明一个名为network的networks,subnet为network的子网地址,默认网关是177.7.0.1
networks:
network:
ipam:
driver: default
config:
- subnet: '177.7.0.0/16'
# 设置mysqlredis持久化保存
volumes:
mysql:
redis:
services:
web:
build:
context: ../../web
dockerfile: ./Dockerfile
container_name: gva-web
restart: always
ports:
- '8080:8080'
depends_on:
- server
command: [ 'nginx-debug', '-g', 'daemon off;' ]
networks:
network:
ipv4_address: 177.7.0.11
server:
build:
context: ../../server
dockerfile: ./Dockerfile
container_name: gva-server
restart: always
ports:
- '8888:8888'
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
links:
- mysql
- redis
networks:
network:
ipv4_address: 177.7.0.12
mysql:
image: mysql:8.0.21 # 如果您是 arm64 架构:如 MacOS 的 M1,请修改镜像为 image: mysql/mysql-server:8.0.21
container_name: gva-mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #设置utf8字符集
restart: always
ports:
- "13306:3306" # host物理直接映射端口为13306
environment:
#MYSQL_ROOT_PASSWORD: 'Aa@6447985' # root管理员用户密码
MYSQL_DATABASE: 'qmPlus' # 初始化启动时要创建的数据库的名称
MYSQL_USER: 'gva'
MYSQL_PASSWORD: 'Aa@6447985'
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "gva", "-pAa@6447985"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- mysql:/var/lib/mysql
networks:
network:
ipv4_address: 177.7.0.13
redis:
image: redis:6.0.6
container_name: gva-redis # 容器名
restart: always
ports:
- '16379:6379'
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG || exit 1"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- redis:/data
networks:
network:
ipv4_address: 177.7.0.14
+18
View File
@@ -0,0 +1,18 @@
FROM centos:7
WORKDIR /opt
ENV LANG=en_US.utf8
COPY deploy/docker/entrypoint.sh .
COPY build/ /usr/share/nginx/html/
COPY server/config.yaml /usr/share/nginx/html/config.yaml
COPY web/.docker-compose/nginx/conf.d/nginx.conf /etc/nginx/conf.d/nginx.conf
RUN set -ex \
&& echo "LANG=en_US.utf8" > /etc/locale.conf \
&& echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf \
&& echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf \
&& yum -y install epel-release \
&& yum -y localinstall http://mirrors.ustc.edu.cn/mysql-repo/mysql57-community-release-el7.rpm \
&& yum -y install mysql-community-server git redis nginx go npm --nogpgcheck && chmod +x ./entrypoint.sh \
&& npm install -g yarn && go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
&& echo "start" > /dev/null
EXPOSE 80
ENTRYPOINT ["./entrypoint.sh"]
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
if [ ! -d "/var/lib/mysql/gva" ]; then
mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql
mysqld --daemonize --user=mysql
sleep 5s
mysql -uroot -e "create database gva default charset 'utf8' collate 'utf8_bin'; grant all on gva.* to 'root'@'127.0.0.1' identified by '123456'; flush privileges;"
else
mysqld --daemonize --user=mysql
fi
redis-server &
if [ "$1" = "actions" ]; then
cd /opt/gva/server && go run main.go &
cd /opt/gva/web/ && yarn serve &
else
/usr/sbin/nginx &
cd /usr/share/nginx/html/ && ./server &
fi
echo "gva ALL start!!!"
tail -f /dev/null
@@ -0,0 +1,148 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: config.yaml
annotations:
flipped-aurora/gin-vue-admin: backend
github: "https://github.com/flipped-aurora/gin-vue-admin.git"
app.kubernetes.io/version: 0.0.1
labels:
app: gva-server
version: gva-vue3
data:
config.yaml: |
# github.com/flipped-aurora/gin-vue-admin/server Global Configuration
# jwt configuration
jwt:
signing-key: 'qmPlus'
expires-time: 604800
buffer-time: 86400
# zap logger configuration
zap:
level: 'info'
format: 'console'
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'
director: 'log'
link-name: 'latest_log'
show-line: true
encode-level: 'LowercaseColorLevelEncoder'
stacktrace-key: 'stacktrace'
log-in-console: true
# redis configuration
redis:
db: 0
addr: '127.0.0.1:6379'
password: ''
# email configuration
email:
to: 'xxx@qq.com'
port: 465
from: 'xxx@163.com'
host: 'smtp.163.com'
is-ssl: true
secret: 'xxx'
nickname: 'test'
# casbin configuration
casbin:
model-path: './resource/rbac_model.conf'
# system configuration
system:
env: 'develop' # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: 'mysql'
oss-type: 'local' # 控制oss选择走本期还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
use-multipoint: false
# captcha configuration
captcha:
key-long: 6
img-width: 240
img-height: 80
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first
mysql:
path: ''
config: ''
db-name: ''
username: ''
password: ''
max-idle-conns: 10
max-open-conns: 100
log-mode: false
log-zap: ""
# local configuration
local:
path: 'uploads/file'
# autocode configuration
autocode:
transfer-restart: true
root: ""
server: /server
server-api: /api/v1/autocode
server-initialize: /initialize
server-model: /model/autocode
server-request: /model/autocode/request/
server-router: /router/autocode
server-service: /service/autocode
web: /web/src
web-api: /api
web-flow: /view
web-form: /view
web-table: /view
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
qiniu:
zone: 'ZoneHuaDong'
bucket: ''
img-path: ''
use-https: false
access-key: ''
secret-key: ''
use-cdn-domains: false
# aliyun oss configuration
aliyun-oss:
endpoint: 'yourEndpoint'
access-key-id: 'yourAccessKeyId'
access-key-secret: 'yourAccessKeySecret'
bucket-name: 'yourBucketName'
bucket-url: 'yourBucketUrl'
base-path: 'yourBasePath'
# tencent cos configuration
tencent-cos:
bucket: 'xxxxx-10005608'
region: 'ap-shanghai'
secret-id: 'xxxxxxxx'
secret-key: 'xxxxxxxx'
base-url: 'https://gin.vue.admin'
path-prefix: 'github.com/flipped-aurora/gin-vue-admin/server'
# excel configuration
excel:
dir: './resource/excel/'
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail: [
# tableName: 需要清理的表名
# compareField: 需要比较时间的字段
# interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数
# 2160h = 24 * 30 * 3 -> 三个月
{ tableName: "sys_operation_records" , compareField: "created_at", interval: "2160h" },
{ tableName: "jwt_blacklists" , compareField: "created_at", interval: "168h" }
#{ tableName: "log2" , compareField: "created_at", interval: "2160h" }
]
@@ -0,0 +1,74 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gva-server
annotations:
flipped-aurora/gin-vue-admin: backend
github: "https://github.com/flipped-aurora/gin-vue-admin.git"
app.kubernetes.io/version: 0.0.1
labels:
app: gva-server
version: gva-vue3
spec:
replicas: 1
selector:
matchLabels:
app: gva-server
version: gva-vue3
template:
metadata:
labels:
app: gva-server
version: gva-vue3
spec:
containers:
- name: gin-vue-admin-container
image: registry.cn-hangzhou.aliyuncs.com/gva/server:latest
imagePullPolicy: Always
ports:
- containerPort: 8888
name: http
volumeMounts:
- mountPath: /go/src/github.com/flipped-aurora/gin-vue-admin/server/config.docker.yaml
name: config
subPath: config.yaml
- mountPath: /etc/localtime
name: localtime
resources:
limits:
cpu: 1000m
memory: 2000Mi
requests:
cpu: 100m
memory: 200Mi
livenessProbe:
failureThreshold: 1
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8888
timeoutSeconds: 1
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8888
timeoutSeconds: 1
startupProbe:
failureThreshold: 40
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 8888
timeoutSeconds: 1
#imagePullSecrets:
# - name: docker-registry
volumes:
- name: localtime
hostPath:
path: /etc/localtime
- name: config
configMap:
name: config.yaml
@@ -0,0 +1,21 @@
apiVersion: v1
kind: Service
metadata:
name: gva-server
annotations:
flipped-aurora/gin-vue-admin: backend
github: "https://github.com/flipped-aurora/gin-vue-admin.git"
app.kubernetes.io/version: 0.0.1
labels:
app: gva-server
version: gva-vue3
spec:
selector:
app: gva-server
version: gva-vue3
ports:
- port: 8888
name: http
targetPort: 8888
type: ClusterIP
# type: NodePort
@@ -0,0 +1,32 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: my.conf
data:
my.conf: |
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/share/nginx/html;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
try_files $uri $uri/ /index.html;
}
location /api {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
rewrite ^/api/(.*)$ /$1 break; #重写
proxy_pass http://gva-server:8888; # 设置代理服务器的协议和地址
}
location /api/swagger/index.html {
proxy_pass http://gva-server:8888/swagger/index.html;
}
}
@@ -0,0 +1,51 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gva-web
annotations:
flipped-aurora/gin-vue-admin: ui
github: "https://github.com/flipped-aurora/gin-vue-admin.git"
app.kubernetes.io/version: 0.0.1
labels:
app: gva-web
version: gva-vue3
spec:
replicas: 1
selector:
matchLabels:
app: gva-web
version: gva-vue3
template:
metadata:
labels:
app: gva-web
version: gva-vue3
spec:
containers:
- name: gin-vue-admin-nginx-container
image: registry.cn-hangzhou.aliyuncs.com/gva/web:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
resources:
limits:
cpu: 500m
memory: 1000Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- mountPath: /etc/nginx/conf.d/
name: nginx-config
volumes:
- name: nginx-config
configMap:
name: my.conf
@@ -0,0 +1,18 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gva-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: demo.gin-vue-admin.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gva-web
port:
number: 8080
@@ -0,0 +1,21 @@
apiVersion: v1
kind: Service
metadata:
name: gva-web
annotations:
flipped-aurora/gin-vue-admin: ui
github: "https://github.com/flipped-aurora/gin-vue-admin.git"
app.kubernetes.io/version: 0.0.1
labels:
app: gva-web
version: gva-vue3
spec:
# type: NodePort
type: ClusterIP
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: gva-web
version: gva-vue3
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

+49
View File
@@ -0,0 +1,49 @@
{
"folders": [
{
"path": "server",
"name": "backend"
},
{
"path": "web",
"name": "frontend"
},
{
"path": ".",
"name": "root"
}
],
"settings": {
"go.toolsEnvVars": {
"GOPROXY": "https://goproxy.cn,direct",
"GONOPROXY": "none;"
}
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"type": "go",
"request": "launch",
"name": "Backend",
"cwd": "${workspaceFolder:backend}",
"program": "${workspaceFolder:backend}/"
},
{
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder:frontend}",
"name": "Frontend",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "serve"]
}
],
"compounds": [
{
"name": "Both (Backend & Frontend)",
"configurations": ["Backend", "Frontend"],
"stopAll": true
}
]
}
}
+33
View File
@@ -0,0 +1,33 @@
FROM golang:alpine as builder
RUN mkdir /app
WORKDIR /app
COPY . .
RUN go env -w GO111MODULE=on \
&& go env -w GOPROXY=https://goproxy.cn,direct \
&& go env -w CGO_ENABLED=0 \
&& go env \
&& go mod tidy \
&& go build -o server .
FROM alpine:latest
LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com"
# 设置时区
ENV TZ=Asia/Shanghai
RUN mkdir /app \
&& apk update && apk add --no-cache tzdata openntpd \
&& ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY --from=0 /app/server ./
COPY --from=0 /app/resource ./resource/
COPY --from=0 /app/config.docker.yaml ./
# 挂载目录:如果使用了sqlite数据库,容器命令示例:docker run -d -v /宿主机路径/gva.db:/app/gva.db -p 8888:8888 --name gva-server-v1 gva-server:1.0
# VOLUME ["/app"]
EXPOSE 8888
ENTRYPOINT ./server -c config.docker.yaml
+54
View File
@@ -0,0 +1,54 @@
## server项目结构
```shell
├── api
│   └── v1
├── config
├── core
├── docs
├── global
├── initialize
│   └── internal
├── middleware
├── model
│   ├── request
│   └── response
├── packfile
├── resource
│   ├── excel
│   ├── page
│   └── template
├── router
├── service
├── source
└── utils
├── timer
└── upload
```
| 文件夹 | 说明 | 描述 |
| ------------ | ----------------------- | --------------------------- |
| `api` | api层 | api层 |
| `--v1` | v1版本接口 | v1版本接口 |
| `config` | 配置包 | config.yaml对应的配置结构体 |
| `core` | 核心文件 | 核心组件(zap, viper, server)的初始化 |
| `docs` | swagger文档目录 | swagger文档目录 |
| `global` | 全局对象 | 全局对象 |
| `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 |
| `--internal` | 初始化内部函数 | gorm 的 longger 自定义,在此文件夹的函数只能由 `initialize` 层进行调用 |
| `middleware` | 中间件层 | 用于存放 `gin` 中间件代码 |
| `model` | 模型层 | 模型对应数据表 |
| `--request` | 入参结构体 | 接收前端发送到后端的数据。 |
| `--response` | 出参结构体 | 返回给前端的数据结构体 |
| `packfile` | 静态文件打包 | 静态文件打包 |
| `resource` | 静态资源文件夹 | 负责存放静态文件 |
| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 |
| `--page` | 表单生成器 | 表单生成器 打包后的dist |
| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 |
| `router` | 路由层 | 路由层 |
| `service` | service层 | 存放业务逻辑问题 |
| `source` | source层 | 存放初始化数据的函数 |
| `utils` | 工具包 | 工具函数封装 |
| `--timer` | timer | 定时器接口封装 |
| `--upload` | oss | oss接口封装 |
+15
View File
@@ -0,0 +1,15 @@
package v1
import (
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/gaia"
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/system"
)
var ApiGroupApp = new(ApiGroup)
type ApiGroup struct {
SystemApiGroup system.ApiGroup
ExampleApiGroup example.ApiGroup
GaiaApiGroup gaia.ApiGroup
}
+13
View File
@@ -0,0 +1,13 @@
package example
import "github.com/flipped-aurora/gin-vue-admin/server/service"
type ApiGroup struct {
CustomerApi
FileUploadAndDownloadApi
}
var (
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
)
@@ -0,0 +1,150 @@
package example
import (
"fmt"
"io"
"mime/multipart"
"strconv"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// BreakpointContinue
// @Tags ExaFileUploadAndDownload
// @Summary 断点续传到服务器
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "an example for breakpoint resume, 断点续传示例"
// @Success 200 {object} response.Response{msg=string} "断点续传到服务器"
// @Router /fileUploadAndDownload/breakpointContinue [post]
func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
fileMd5 := c.Request.FormValue("fileMd5")
fileName := c.Request.FormValue("fileName")
chunkMd5 := c.Request.FormValue("chunkMd5")
chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber"))
chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal"))
_, FileHeader, err := c.Request.FormFile("file")
if err != nil {
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
response.FailWithMessage("接收文件失败", c)
return
}
f, err := FileHeader.Open()
if err != nil {
global.GVA_LOG.Error("文件读取失败!", zap.Error(err))
response.FailWithMessage("文件读取失败", c)
return
}
defer func(f multipart.File) {
err := f.Close()
if err != nil {
fmt.Println(err)
}
}(f)
cen, _ := io.ReadAll(f)
if !utils.CheckMd5(cen, chunkMd5) {
global.GVA_LOG.Error("检查md5失败!", zap.Error(err))
response.FailWithMessage("检查md5失败", c)
return
}
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
if err != nil {
global.GVA_LOG.Error("查找或创建记录失败!", zap.Error(err))
response.FailWithMessage("查找或创建记录失败", c)
return
}
pathC, err := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5)
if err != nil {
global.GVA_LOG.Error("断点续传失败!", zap.Error(err))
response.FailWithMessage("断点续传失败", c)
return
}
if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathC, chunkNumber); err != nil {
global.GVA_LOG.Error("创建文件记录失败!", zap.Error(err))
response.FailWithMessage("创建文件记录失败", c)
return
}
response.OkWithMessage("切片创建成功", c)
}
// FindFile
// @Tags ExaFileUploadAndDownload
// @Summary 查找文件
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "Find the file, 查找文件"
// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
// @Router /fileUploadAndDownload/findFile [get]
func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
fileMd5 := c.Query("fileMd5")
fileName := c.Query("fileName")
chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal"))
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
if err != nil {
global.GVA_LOG.Error("查找失败!", zap.Error(err))
response.FailWithMessage("查找失败", c)
} else {
response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c)
}
}
// BreakpointContinueFinish
// @Tags ExaFileUploadAndDownload
// @Summary 创建文件
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "上传文件完成"
// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
// @Router /fileUploadAndDownload/findFile [post]
func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
fileMd5 := c.Query("fileMd5")
fileName := c.Query("fileName")
filePath, err := utils.MakeFile(fileName, fileMd5)
if err != nil {
global.GVA_LOG.Error("文件创建失败!", zap.Error(err))
response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
} else {
response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c)
}
}
// RemoveChunk
// @Tags ExaFileUploadAndDownload
// @Summary 删除切片
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "删除缓存切片"
// @Success 200 {object} response.Response{msg=string} "删除切片"
// @Router /fileUploadAndDownload/removeChunk [post]
func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
var file example.ExaFile
err := c.ShouldBindJSON(&file)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.RemoveChunk(file.FileMd5)
if err != nil {
global.GVA_LOG.Error("缓存切片删除失败!", zap.Error(err))
return
}
err = fileUploadAndDownloadService.DeleteFileChunk(file.FileMd5, file.FilePath)
if err != nil {
global.GVA_LOG.Error(err.Error(), zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("缓存切片删除成功", c)
}
+176
View File
@@ -0,0 +1,176 @@
package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type CustomerApi struct{}
// CreateExaCustomer
// @Tags ExaCustomer
// @Summary 创建客户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
// @Success 200 {object} response.Response{msg=string} "创建客户"
// @Router /customer/customer [post]
func (e *CustomerApi) CreateExaCustomer(c *gin.Context) {
var customer example.ExaCustomer
err := c.ShouldBindJSON(&customer)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(customer, utils.CustomerVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
customer.SysUserID = utils.GetUserID(c)
customer.SysUserAuthorityID = utils.GetUserAuthorityId(c)
err = customerService.CreateExaCustomer(customer)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteExaCustomer
// @Tags ExaCustomer
// @Summary 删除客户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body example.ExaCustomer true "客户ID"
// @Success 200 {object} response.Response{msg=string} "删除客户"
// @Router /customer/customer [delete]
func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) {
var customer example.ExaCustomer
err := c.ShouldBindJSON(&customer)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = customerService.DeleteExaCustomer(customer)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateExaCustomer
// @Tags ExaCustomer
// @Summary 更新客户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body example.ExaCustomer true "客户ID, 客户信息"
// @Success 200 {object} response.Response{msg=string} "更新客户信息"
// @Router /customer/customer [put]
func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) {
var customer example.ExaCustomer
err := c.ShouldBindJSON(&customer)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(customer, utils.CustomerVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = customerService.UpdateExaCustomer(&customer)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// GetExaCustomer
// @Tags ExaCustomer
// @Summary 获取单一客户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query example.ExaCustomer true "客户ID"
// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情"
// @Router /customer/customer [get]
func (e *CustomerApi) GetExaCustomer(c *gin.Context) {
var customer example.ExaCustomer
err := c.ShouldBindQuery(&customer)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
data, err := customerService.GetExaCustomer(customer.ID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
}
// GetExaCustomerList
// @Tags ExaCustomer
// @Summary 分页获取权限客户列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.PageInfo true "页码, 每页大小"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量"
// @Router /customer/customerList [get]
func (e *CustomerApi) GetExaCustomerList(c *gin.Context) {
var pageInfo request.PageInfo
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(pageInfo, utils.PageInfoVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: customerList,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
@@ -0,0 +1,133 @@
package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
exampleRes "github.com/flipped-aurora/gin-vue-admin/server/model/example/response"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type FileUploadAndDownloadApi struct{}
// UploadFile
// @Tags ExaFileUploadAndDownload
// @Summary 上传文件示例
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "上传文件示例"
// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
// @Router /fileUploadAndDownload/upload [post]
func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
var file example.ExaFileUploadAndDownload
noSave := c.DefaultQuery("noSave", "0")
_, header, err := c.Request.FormFile("file")
if err != nil {
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
response.FailWithMessage("接收文件失败", c)
return
}
file, err = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径
if err != nil {
global.GVA_LOG.Error("上传文件失败!", zap.Error(err))
response.FailWithMessage("上传文件失败", c)
return
}
response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
}
// EditFileName 编辑文件名或者备注
func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
var file example.ExaFileUploadAndDownload
err := c.ShouldBindJSON(&file)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = fileUploadAndDownloadService.EditFileName(file)
if err != nil {
global.GVA_LOG.Error("编辑失败!", zap.Error(err))
response.FailWithMessage("编辑失败", c)
return
}
response.OkWithMessage("编辑成功", c)
}
// DeleteFile
// @Tags ExaFileUploadAndDownload
// @Summary 删除文件
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
// @Success 200 {object} response.Response{msg=string} "删除文件"
// @Router /fileUploadAndDownload/deleteFile [post]
func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
var file example.ExaFileUploadAndDownload
err := c.ShouldBindJSON(&file)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := fileUploadAndDownloadService.DeleteFile(file); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// GetFileList
// @Tags ExaFileUploadAndDownload
// @Summary 分页文件列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "页码, 每页大小"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
// @Router /fileUploadAndDownload/getFileList [post]
func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
var pageInfo request.PageInfo
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// ImportURL
// @Tags ExaFileUploadAndDownload
// @Summary 导入URL
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body example.ExaFileUploadAndDownload true "对象"
// @Success 200 {object} response.Response{msg=string} "导入URL"
// @Router /fileUploadAndDownload/importURL [post]
func (b *FileUploadAndDownloadApi) ImportURL(c *gin.Context) {
var file []example.ExaFileUploadAndDownload
err := c.ShouldBindJSON(&file)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := fileUploadAndDownloadService.ImportURL(&file); err != nil {
global.GVA_LOG.Error("导入URL失败!", zap.Error(err))
response.FailWithMessage("导入URL失败", c)
return
}
response.OkWithMessage("导入URL成功", c)
}
+159
View File
@@ -0,0 +1,159 @@
package gaia
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
gaiaReq "github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type DashboardApi struct{}
// GetAccountQuotaRankingData 分页获取账号额度排名
// @Tags Dashboard
// @Summary 分页获取dashboard表列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /dashboard/getAccountQuotaRankingData [get]
func (dashboardApi *DashboardApi) GetAccountQuotaRankingData(c *gin.Context) {
var pageInfo gaiaReq.GetAccountQuotaRankingDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := dashboardService.GetAccountQuotaRankingData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetAppQuotaRankingData 分页获取【应用】配额排名数据
// @Tags Dashboard
// @Summary 分页获取dashboard表列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取【应用】配额排名数据"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /dashboard/getAppQuotaRankingData [get]
func (dashboardApi *DashboardApi) GetAppQuotaRankingData(c *gin.Context) {
var pageInfo gaiaReq.GetAppQuotaRankingDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := dashboardService.GetAppQuotaRankingData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetAppTokenQuotaRankingData 分页获取【应用密钥】配额排名数据列表
// @Tags Dashboard
// @Summary 分页获取dashboard表列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取【应用密钥】配额排名数据列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /dashboard/getAppTokenQuotaRankingData [get]
func (dashboardApi *DashboardApi) GetAppTokenQuotaRankingData(c *gin.Context) {
var pageInfo gaiaReq.GetAppTokenQuotaRankingDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := dashboardService.GetAppTokenQuotaRankingData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetAppTokenDailyQuotaData 获取每天密钥花费数据列表
// @Tags Dashboard
// @Summary 分页获取dashboard表列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "获取每天密钥花费数据列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /dashboard/getAppTokenDailyQuotaData [get]
func (dashboardApi *DashboardApi) GetAppTokenDailyQuotaData(c *gin.Context) {
var pageInfo gaiaReq.GetAppTokenDailyQuotaDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := dashboardService.GetAppTokenDailyQuotaData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetAiImageQuotaRankingData 获取每天ai图片花费数据列表
// @Tags Dashboard
// @Summary 获取每天ai图片花费数据列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "获取每天ai图片花费数据列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /dashboard/getAppTokenDailyQuotaData [get]
func (dashboardApi *DashboardApi) GetAiImageQuotaRankingData(c *gin.Context) {
var pageInfo gaiaReq.GetAiImageQuotaRankingDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := dashboardService.GetAiImageQuotaRankingData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
+19
View File
@@ -0,0 +1,19 @@
package gaia
import "github.com/flipped-aurora/gin-vue-admin/server/service"
type ApiGroup struct {
DashboardApi
QuotaApi
TenantsApi
SystemApi
TestApi
}
var (
dashboardService = service.ServiceGroupApp.GaiaServiceGroup.DashboardService
tenantsService = service.ServiceGroupApp.GaiaServiceGroup.TenantsService
systemIntegratedService = service.ServiceGroupApp.GaiaServiceGroup.SystemIntegratedService
)
var QuotaService = service.ServiceGroupApp.GaiaServiceGroup.QuotaService
var TestService = service.ServiceGroupApp.GaiaServiceGroup.TestService
+73
View File
@@ -0,0 +1,73 @@
package gaia
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
gaiaReq "github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid/v5"
"go.uber.org/zap"
)
type QuotaApi struct{}
// QuotaManagementList
// @Tags Quota
// @Summary 额度管理列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/quota/getManagementList [get]
func (quotaApi *QuotaApi) QuotaManagementList(c *gin.Context) {
var pageInfo gaiaReq.GetAccountQuotaRankingDataReq
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := QuotaService.GetQuotaManagementData(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// SetUserQuota
// @Tags Quota
// @Summary 设置用户额度
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/quota/setUserQuota [post]
func (quotaApi *QuotaApi) SetUserQuota(c *gin.Context) {
var err error
var uid uuid.UUID
var pageInfo gaiaReq.SetUserQuotaRequest
if err = c.ShouldBindJSON(&pageInfo); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
//
if uid, err = uuid.FromString(pageInfo.Uid); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err = QuotaService.SetUserQuota(uid, pageInfo.Quota); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed("ok", "修改成功", c)
}
+51
View File
@@ -0,0 +1,51 @@
package gaia
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/gaia"
"github.com/gin-gonic/gin"
)
type SystemApi struct{}
// GetDingTalk 获取钉钉系统配置
// @Tags System
// @Summary 获取钉钉系统配置
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaia.Tenants true "用id查询tenants表"
// @Success 200 {object} response.Response{data=gaia.Tenants,msg=string} "查询成功"
// @Router /gaia/system/dingtalk [get]
func (systemApi *SystemApi) GetDingTalk(c *gin.Context) {
var config = make(map[string]interface{})
config["host"] = global.GVA_CONFIG.Gaia.Url
config["config"] = systemIntegratedService.GetIntegratedConfig(gaia.SystemIntegrationDingTalk)
response.OkWithData(config, c)
}
// SetDingTalk 设置钉钉系统配置
// @Tags System
// @Summary 设置钉钉系统配置
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaia.Tenants true "用id查询tenants表"
// @Success 200 {object} response.Response{data=gaia.Tenants,msg=string} "查询成功"
// @Router /gaia/system/dingtalk [post]
func (systemApi *SystemApi) SetDingTalk(c *gin.Context) {
var err error
var req gaia.SystemIntegration
if err = c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// update
req.Classify = gaia.SystemIntegrationDingTalk
if err = systemIntegratedService.SetIntegratedConfig(req, req.Test); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData("ok", c)
}
+80
View File
@@ -0,0 +1,80 @@
package gaia
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
gaiaReq "github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type TenantsApi struct{}
// FindTenants 用id查询tenants表
// @Tags Tenants
// @Summary 用id查询tenants表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaia.Tenants true "用id查询tenants表"
// @Success 200 {object} response.Response{data=gaia.Tenants,msg=string} "查询成功"
// @Router /tenants/findTenants [get]
func (tenantsApi *TenantsApi) FindTenants(c *gin.Context) {
id := c.Query("id")
retenants, err := tenantsService.GetTenants(id)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(retenants, c)
}
// GetTenantsList 分页获取tenants表列表
// @Tags Tenants
// @Summary 分页获取tenants表列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.TenantsSearch true "分页获取tenants表列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /tenants/getTenantsList [get]
func (tenantsApi *TenantsApi) GetTenantsList(c *gin.Context) {
var pageInfo gaiaReq.TenantsSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := tenantsService.GetTenantsInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetAllTenants 获取所有工作区
// @Tags Tenants
// @Summary 获取所有工作区
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaia.Tenants true "获取所有工作区"
// @Success 200 {object} response.Response{data=gaia.Tenants,msg=string} "查询成功"
// @Router /tenants/getAllTenants [get]
func (tenantsApi *TenantsApi) GetAllTenants(c *gin.Context) {
retenants, err := tenantsService.GetAllTenants()
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(retenants, c)
}
+141
View File
@@ -0,0 +1,141 @@
package gaia
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/gaia"
"github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type TestApi struct{}
// SyncDatabaseTableData
// @Tags Test
// @Summary 同步数据库表数据
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/test/sync/database [post]
func (quotaApi *TestApi) SyncDatabaseTableData(c *gin.Context) {
var data request.SyncDatabaseTableData
if err := c.ShouldBindJSON(&data); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 是否都不为空
if len(data.LogTable) > 0 && len(data.NewTable) > 0 && len(data.KeyName) > 0 && len(data.OrderName) > 0 {
go TestService.SyncDatabaseTableData(data.LogTable, data.NewTable, data.KeyName, data.OrderName, data.GroupName)
response.OkWithDetailed("ok", "获取成功", c)
} else {
response.FailWithMessage("传参有误", c)
}
}
// GaiaAppRequestTest
// @Tags Test
// @Summary 发起gaia应用请求测试
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/test/app/request [post]
func (quotaApi *TestApi) GaiaAppRequestTest(c *gin.Context) {
err := TestService.AppRequestTest()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed("ok", "获取成功", c)
}
// GaiaAppRequestTestList
// @Tags Test
// @Summary gaia应用请求测试结果列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/test/app/request/list [get]
func (quotaApi *TestApi) GaiaAppRequestTestList(c *gin.Context) {
var pageInfo request.GetAppRequestTestRequest
if err := c.ShouldBindQuery(&pageInfo); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// list
lock, list, total, err := TestService.AppRequestTestList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
var appIdList []string
var appList []map[string]string
var batchInfo []gaia.AppRequestTest
if err = global.GVA_DB.Select("app_id").Where("batch_id = ?", pageInfo.BatchId).Group(
"app_id").Find(&batchInfo).Error; err == nil {
for _, v := range batchInfo {
appIdList = append(appIdList, v.AppID)
}
}
// 查询相关的app列表
if len(appIdList) > 0 {
var apps []gaia.Apps
if err = global.GVA_DB.Select("id", "name").Where(
"id IN (?)", appIdList).Find(&apps).Error; err == nil {
for _, v := range apps {
appList = append(appList, map[string]string{
"value": v.ID.String(),
"label": v.Name,
})
}
}
}
response.OkWithDetailed(map[string]interface{}{
"lock": lock,
"list": list,
"total": total,
"apps": appList,
"page": pageInfo.Page,
"page_size": pageInfo.PageSize,
}, "获取成功", c)
}
// GaiaAppRequestTestBatch
// @Tags Test
// @Summary gaia应用请求测试批次列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query gaiaReq.DashboardSearch true "分页获取账号额度排名列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /gaia/test/app/request/batch [get]
func (quotaApi *TestApi) GaiaAppRequestTestBatch(c *gin.Context) {
var pageInfo request.GetAppRequestTestRequest
if err := c.ShouldBindQuery(&pageInfo); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// list
lock, list, total, err := TestService.AppRequestTestBatch(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(map[string]interface{}{
"lock": lock,
"list": list,
"total": total,
"page": pageInfo.Page,
"page_size": pageInfo.PageSize,
}, "获取成功", c)
}
@@ -0,0 +1,115 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
request "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AutoCodeHistoryApi struct{}
// First
// @Tags AutoCode
// @Summary 获取meta信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "请求参数"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息"
// @Router /autoCode/getMeta [post]
func (a *AutoCodeHistoryApi) First(c *gin.Context) {
var info common.GetById
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
data, err := autoCodeHistoryService.First(c.Request.Context(), info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
}
// Delete
// @Tags AutoCode
// @Summary 删除回滚记录
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "请求参数"
// @Success 200 {object} response.Response{msg=string} "删除回滚记录"
// @Router /autoCode/delSysHistory [post]
func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
var info common.GetById
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodeHistoryService.Delete(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// RollBack
// @Tags AutoCode
// @Summary 回滚自动生成代码
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.SysAutoHistoryRollBack true "请求参数"
// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
// @Router /autoCode/rollback [post]
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
var info request.SysAutoHistoryRollBack
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodeHistoryService.RollBack(c.Request.Context(), info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("回滚成功", c)
}
// GetList
// @Tags AutoCode
// @Summary 查询回滚记录
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body common.PageInfo true "请求参数"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
// @Router /autoCode/getSysHistory [post]
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
var info common.PageInfo
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := autoCodeHistoryService.GetList(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: info.Page,
PageSize: info.PageSize,
}, "获取成功", c)
}
@@ -0,0 +1,100 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
common "github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"strings"
)
type AutoCodePackageApi struct{}
// Create
// @Tags AutoCodePackage
// @Summary 创建package
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.SysAutoCodePackageCreate true "创建package"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
// @Router /autoCode/createPackage [post]
func (a *AutoCodePackageApi) Create(c *gin.Context) {
var info request.SysAutoCodePackageCreate
_ = c.ShouldBindJSON(&info)
if err := utils.Verify(info, utils.AutoPackageVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if strings.Contains(info.PackageName, "\\") || strings.Contains(info.PackageName, "/") || strings.Contains(info.PackageName, "..") {
response.FailWithMessage("包名不合法", c)
return
} // PackageName可能导致路径穿越的问题 / 和 \ 都要防止
err := autoCodePackageService.Create(c.Request.Context(), &info)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// Delete
// @Tags AutoCode
// @Summary 删除package
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body common.GetById true "创建package"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
// @Router /autoCode/delPackage [post]
func (a *AutoCodePackageApi) Delete(c *gin.Context) {
var info common.GetById
_ = c.ShouldBindJSON(&info)
err := autoCodePackageService.Delete(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// All
// @Tags AutoCodePackage
// @Summary 获取package
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
// @Router /autoCode/getPackage [post]
func (a *AutoCodePackageApi) All(c *gin.Context) {
data, err := autoCodePackageService.All(c.Request.Context())
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"pkgs": data}, "获取成功", c)
}
// Templates
// @Tags AutoCodePackage
// @Summary 获取package
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
// @Router /autoCode/getTemplates [get]
func (a *AutoCodePackageApi) Templates(c *gin.Context) {
data, err := autoCodePackageService.Templates(c.Request.Context())
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(data, "获取成功", c)
}
@@ -0,0 +1,119 @@
package system
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AutoCodePluginApi struct{}
// Install
// @Tags AutoCodePlugin
// @Summary 安装插件
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param plug formData file true "this is a test file"
// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功"
// @Router /autoCode/installPlugin [post]
func (a *AutoCodePluginApi) Install(c *gin.Context) {
header, err := c.FormFile("plug")
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
web, server, err := autoCodePluginService.Install(header)
webStr := "web插件安装成功"
serverStr := "server插件安装成功"
if web == -1 {
webStr = "web端插件未成功安装,请按照文档自行解压安装,如果为纯后端插件请忽略此条提示"
}
if server == -1 {
serverStr = "server端插件未成功安装,请按照文档自行解压安装,如果为纯前端插件请忽略此条提示"
}
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData([]interface{}{
gin.H{
"code": web,
"msg": webStr,
},
gin.H{
"code": server,
"msg": serverStr,
}}, c)
}
// Packaged
// @Tags AutoCodePlugin
// @Summary 打包插件
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param plugName query string true "插件名称"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
// @Router /autoCode/pubPlug [post]
func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
plugName := c.Query("plugName")
zipPath, err := autoCodePluginService.PubPlug(plugName)
if err != nil {
global.GVA_LOG.Error("打包失败!", zap.Error(err))
response.FailWithMessage("打包失败"+err.Error(), c)
return
}
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
}
// Packaged
// @Tags AutoCodePlugin
// @Summary 打包插件
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
// @Router /autoCode/initMenu [post]
func (a *AutoCodePluginApi) InitMenu(c *gin.Context) {
var menuInfo request.InitMenu
err := c.ShouldBindJSON(&menuInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodePluginService.InitMenu(menuInfo)
if err != nil {
global.GVA_LOG.Error("创建初始化Menu失败!", zap.Error(err))
response.FailWithMessage("创建初始化Menu失败"+err.Error(), c)
return
}
response.OkWithMessage("文件变更成功", c)
}
// Packaged
// @Tags AutoCodePlugin
// @Summary 打包插件
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
// @Router /autoCode/initAPI [post]
func (a *AutoCodePluginApi) InitAPI(c *gin.Context) {
var apiInfo request.InitApi
err := c.ShouldBindJSON(&apiInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodePluginService.InitAPI(apiInfo)
if err != nil {
global.GVA_LOG.Error("创建初始化API失败!", zap.Error(err))
response.FailWithMessage("创建初始化API失败"+err.Error(), c)
return
}
response.OkWithMessage("文件变更成功", c)
}
@@ -0,0 +1,121 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AutoCodeTemplateApi struct{}
// Preview
// @Tags AutoCodeTemplate
// @Summary 预览创建后的代码
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.AutoCode true "预览创建代码"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
// @Router /autoCode/preview [post]
func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
var info request.AutoCode
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(info, utils.AutoCodeVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = info.Pretreatment()
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
info.PackageT = utils.FirstUpper(info.Package)
autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("预览失败!", zap.Error(err))
response.FailWithMessage("预览失败", c)
} else {
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
}
}
// Create
// @Tags AutoCodeTemplate
// @Summary 自动代码模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.AutoCode true "创建自动代码"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/createTemp [post]
func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
var info request.AutoCode
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(info, utils.AutoCodeVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = info.Pretreatment()
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = autoCodeTemplateService.Create(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
response.OkWithMessage("创建成功", c)
}
}
// Create
// @Tags AddFunc
// @Summary 增加方法
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.AutoCode true "增加方法"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/addFunc [post]
func (a *AutoCodeTemplateApi) AddFunc(c *gin.Context) {
var info request.AutoFunc
err := c.ShouldBindJSON(&info)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
var tempMap map[string]string
if info.IsPreview {
info.Router = "填充router"
info.FuncName = "填充funcName"
info.Method = "填充method"
info.Description = "填充description"
tempMap, err = autoCodeTemplateService.GetApiAndServer(info)
} else {
err = autoCodeTemplateService.AddFunc(info)
}
if err != nil {
global.GVA_LOG.Error("注入失败!", zap.Error(err))
response.FailWithMessage("注入失败", c)
} else {
if info.IsPreview {
response.OkWithDetailed(tempMap, "注入成功", c)
return
}
response.OkWithMessage("注入成功", c)
}
}
+48
View File
@@ -0,0 +1,48 @@
package system
import "github.com/flipped-aurora/gin-vue-admin/server/service"
type ApiGroup struct {
DBApi
JwtApi
BaseApi
SystemApi
CasbinApi
AutoCodeApi
SystemApiApi
AuthorityApi
DictionaryApi
AuthorityMenuApi
OperationRecordApi
DictionaryDetailApi
AuthorityBtnApi
SysExportTemplateApi
AutoCodePluginApi
AutoCodePackageApi
AutoCodeHistoryApi
AutoCodeTemplateApi
SysParamsApi
}
var (
apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService
jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
userExtendService = service.ServiceGroupApp.SystemServiceGroup.UserExtendService
initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService
casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
autoCodePluginService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePlugin
autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory
autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
)
+323
View File
@@ -0,0 +1,323 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SystemApiApi struct{}
// CreateApi
// @Tags SysApi
// @Summary 创建基础api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
// @Success 200 {object} response.Response{msg=string} "创建基础api"
// @Router /api/createApi [post]
func (s *SystemApiApi) CreateApi(c *gin.Context) {
var api system.SysApi
err := c.ShouldBindJSON(&api)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(api, utils.ApiVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.CreateApi(api)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// SyncApi
// @Tags SysApi
// @Summary 同步API
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "同步API"
// @Router /api/syncApi [get]
func (s *SystemApiApi) SyncApi(c *gin.Context) {
newApis, deleteApis, ignoreApis, err := apiService.SyncApi()
if err != nil {
global.GVA_LOG.Error("同步失败!", zap.Error(err))
response.FailWithMessage("同步失败", c)
return
}
response.OkWithData(gin.H{
"newApis": newApis,
"deleteApis": deleteApis,
"ignoreApis": ignoreApis,
}, c)
}
// GetApiGroups
// @Tags SysApi
// @Summary 获取API分组
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "获取API分组"
// @Router /api/getApiGroups [get]
func (s *SystemApiApi) GetApiGroups(c *gin.Context) {
groups, apiGroupMap, err := apiService.GetApiGroups()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithData(gin.H{
"groups": groups,
"apiGroupMap": apiGroupMap,
}, c)
}
// IgnoreApi
// @Tags IgnoreApi
// @Summary 忽略API
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "同步API"
// @Router /api/ignoreApi [post]
func (s *SystemApiApi) IgnoreApi(c *gin.Context) {
var ignoreApi system.SysIgnoreApi
err := c.ShouldBindJSON(&ignoreApi)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.IgnoreApi(ignoreApi)
if err != nil {
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
response.FailWithMessage("忽略失败", c)
return
}
response.Ok(c)
}
// EnterSyncApi
// @Tags SysApi
// @Summary 确认同步API
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "确认同步API"
// @Router /api/enterSyncApi [post]
func (s *SystemApiApi) EnterSyncApi(c *gin.Context) {
var syncApi systemRes.SysSyncApis
err := c.ShouldBindJSON(&syncApi)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.EnterSyncApi(syncApi)
if err != nil {
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
response.FailWithMessage("忽略失败", c)
return
}
response.Ok(c)
}
// DeleteApi
// @Tags SysApi
// @Summary 删除api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysApi true "ID"
// @Success 200 {object} response.Response{msg=string} "删除api"
// @Router /api/deleteApi [post]
func (s *SystemApiApi) DeleteApi(c *gin.Context) {
var api system.SysApi
err := c.ShouldBindJSON(&api)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(api.GVA_MODEL, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.DeleteApi(api)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// GetApiList
// @Tags SysApi
// @Summary 分页获取API列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.SearchApiParams true "分页获取API列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取API列表,返回包括列表,总数,页码,每页数量"
// @Router /api/getApiList [post]
func (s *SystemApiApi) GetApiList(c *gin.Context) {
var pageInfo systemReq.SearchApiParams
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetApiById
// @Tags SysApi
// @Summary 根据id获取api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "根据id获取api"
// @Success 200 {object} response.Response{data=systemRes.SysAPIResponse} "根据id获取api,返回包括api详情"
// @Router /api/getApiById [post]
func (s *SystemApiApi) GetApiById(c *gin.Context) {
var idInfo request.GetById
err := c.ShouldBindJSON(&idInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(idInfo, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
api, err := apiService.GetApiById(idInfo.ID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c)
}
// UpdateApi
// @Tags SysApi
// @Summary 修改基础api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
// @Success 200 {object} response.Response{msg=string} "修改基础api"
// @Router /api/updateApi [post]
func (s *SystemApiApi) UpdateApi(c *gin.Context) {
var api system.SysApi
err := c.ShouldBindJSON(&api)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(api, utils.ApiVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.UpdateApi(api)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage("修改失败", c)
return
}
response.OkWithMessage("修改成功", c)
}
// GetAllApis
// @Tags SysApi
// @Summary 获取所有的Api 不分页
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=systemRes.SysAPIListResponse,msg=string} "获取所有的Api 不分页,返回包括api列表"
// @Router /api/getAllApis [post]
func (s *SystemApiApi) GetAllApis(c *gin.Context) {
authorityID := utils.GetUserAuthorityId(c)
apis, err := apiService.GetAllApis(authorityID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
}
// DeleteApisByIds
// @Tags SysApi
// @Summary 删除选中Api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "ID"
// @Success 200 {object} response.Response{msg=string} "删除选中Api"
// @Router /api/deleteApisByIds [delete]
func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
var ids request.IdsReq
err := c.ShouldBindJSON(&ids)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = apiService.DeleteApisByIds(ids)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// FreshCasbin
// @Tags SysApi
// @Summary 刷新casbin缓存
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "刷新成功"
// @Router /api/freshCasbin [get]
func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
err := casbinService.FreshCasbin()
if err != nil {
global.GVA_LOG.Error("刷新失败!", zap.Error(err))
response.FailWithMessage("刷新失败", c)
return
}
response.OkWithMessage("刷新成功", c)
}
+202
View File
@@ -0,0 +1,202 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AuthorityApi struct{}
// CreateAuthority
// @Tags Authority
// @Summary 创建角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情"
// @Router /authority/createAuthority [post]
func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
var authority, authBack system.SysAuthority
var err error
if err = c.ShouldBindJSON(&authority); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err = utils.Verify(authority, utils.AuthorityVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if *authority.ParentId == 0 && global.GVA_CONFIG.System.UseStrictAuth {
authority.ParentId = utils.Pointer(utils.GetUserAuthorityId(c))
}
if authBack, err = authorityService.CreateAuthority(authority); err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败"+err.Error(), c)
return
}
err = casbinService.FreshCasbin()
if err != nil {
global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err))
response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c)
return
}
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
}
// CopyAuthority
// @Tags Authority
// @Summary 拷贝角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id"
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "拷贝角色,返回包括系统角色详情"
// @Router /authority/copyAuthority [post]
func (a *AuthorityApi) CopyAuthority(c *gin.Context) {
var copyInfo systemRes.SysAuthorityCopyResponse
err := c.ShouldBindJSON(&copyInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(copyInfo, utils.OldAuthorityVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(copyInfo.Authority, utils.AuthorityVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
adminAuthorityID := utils.GetUserAuthorityId(c)
authBack, err := authorityService.CopyAuthority(adminAuthorityID, copyInfo)
if err != nil {
global.GVA_LOG.Error("拷贝失败!", zap.Error(err))
response.FailWithMessage("拷贝失败"+err.Error(), c)
return
}
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c)
}
// DeleteAuthority
// @Tags Authority
// @Summary 删除角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAuthority true "删除角色"
// @Success 200 {object} response.Response{msg=string} "删除角色"
// @Router /authority/deleteAuthority [post]
func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
var authority system.SysAuthority
var err error
if err = c.ShouldBindJSON(&authority); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 删除角色之前需要判断是否有用户正在使用此角色
if err = authorityService.DeleteAuthority(&authority); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败"+err.Error(), c)
return
}
_ = casbinService.FreshCasbin()
response.OkWithMessage("删除成功", c)
}
// UpdateAuthority
// @Tags Authority
// @Summary 更新角色信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "更新角色信息,返回包括系统角色详情"
// @Router /authority/updateAuthority [put]
func (a *AuthorityApi) UpdateAuthority(c *gin.Context) {
var auth system.SysAuthority
err := c.ShouldBindJSON(&auth)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(auth, utils.AuthorityVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
authority, err := authorityService.UpdateAuthority(auth)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败"+err.Error(), c)
return
}
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
}
// GetAuthorityList
// @Tags Authority
// @Summary 分页获取角色列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "页码, 每页大小"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取角色列表,返回包括列表,总数,页码,每页数量"
// @Router /authority/getAuthorityList [post]
func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
authorityID := utils.GetUserAuthorityId(c)
list, err := authorityService.GetAuthorityInfoList(authorityID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败"+err.Error(), c)
return
}
response.OkWithDetailed(list, "获取成功", c)
}
// SetDataAuthority
// @Tags Authority
// @Summary 设置角色资源权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAuthority true "设置角色资源权限"
// @Success 200 {object} response.Response{msg=string} "设置角色资源权限"
// @Router /authority/setDataAuthority [post]
func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
var auth system.SysAuthority
err := c.ShouldBindJSON(&auth)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(auth, utils.AuthorityIdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
adminAuthorityID := utils.GetUserAuthorityId(c)
err = authorityService.SetDataAuthority(adminAuthorityID, auth)
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败"+err.Error(), c)
return
}
response.OkWithMessage("设置成功", c)
}
@@ -0,0 +1,80 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AuthorityBtnApi struct{}
// GetAuthorityBtn
// @Tags AuthorityBtn
// @Summary 获取权限按钮
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
// @Success 200 {object} response.Response{data=response.SysAuthorityBtnRes,msg=string} "返回列表成功"
// @Router /authorityBtn/getAuthorityBtn [post]
func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
var req request.SysAuthorityBtnReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
res, err := authorityBtnService.GetAuthorityBtn(req)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
return
}
response.OkWithDetailed(res, "查询成功", c)
}
// SetAuthorityBtn
// @Tags AuthorityBtn
// @Summary 设置权限按钮
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
// @Success 200 {object} response.Response{msg=string} "返回列表成功"
// @Router /authorityBtn/setAuthorityBtn [post]
func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) {
var req request.SysAuthorityBtnReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = authorityBtnService.SetAuthorityBtn(req)
if err != nil {
global.GVA_LOG.Error("分配失败!", zap.Error(err))
response.FailWithMessage("分配失败", c)
return
}
response.OkWithMessage("分配成功", c)
}
// CanRemoveAuthorityBtn
// @Tags AuthorityBtn
// @Summary 设置权限按钮
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "删除成功"
// @Router /authorityBtn/canRemoveAuthorityBtn [post]
func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) {
id := c.Query("id")
err := authorityBtnService.CanRemoveAuthorityBtn(id)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
+155
View File
@@ -0,0 +1,155 @@
package system
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
"github.com/goccy/go-json"
"io"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AutoCodeApi struct{}
// GetDB
// @Tags AutoCode
// @Summary 获取当前所有数据库
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库"
// @Router /autoCode/getDB [get]
func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
businessDB := c.Query("businessDB")
dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB)
var dbList []map[string]interface{}
for _, db := range global.GVA_CONFIG.DBList {
var item = make(map[string]interface{})
item["aliasName"] = db.AliasName
item["dbName"] = db.Dbname
item["disable"] = db.Disable
item["dbtype"] = db.Type
dbList = append(dbList, item)
}
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c)
}
}
// GetTables
// @Tags AutoCode
// @Summary 获取当前数据库所有表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表"
// @Router /autoCode/getTables [get]
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
dbName := c.Query("dbName")
businessDB := c.Query("businessDB")
if dbName == "" {
dbName = *global.GVA_ACTIVE_DBNAME
if businessDB != "" {
for _, db := range global.GVA_CONFIG.DBList {
if db.AliasName == businessDB {
dbName = db.Dbname
}
}
}
}
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
if err != nil {
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
response.FailWithMessage("查询table失败", c)
} else {
response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c)
}
}
// GetColumn
// @Tags AutoCode
// @Summary 获取当前表所有字段
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段"
// @Router /autoCode/getColumn [get]
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
businessDB := c.Query("businessDB")
dbName := c.Query("dbName")
if dbName == "" {
dbName = *global.GVA_ACTIVE_DBNAME
if businessDB != "" {
for _, db := range global.GVA_CONFIG.DBList {
if db.AliasName == businessDB {
dbName = db.Dbname
}
}
}
}
tableName := c.Query("tableName")
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
}
}
func (autoApi *AutoCodeApi) LLMAuto(c *gin.Context) {
var llm common.JSONMap
err := c.ShouldBindJSON(&llm)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if global.GVA_CONFIG.AutoCode.AiPath == "" {
response.FailWithMessage("请先前往插件市场个人中心获取AiPath并填入config.yaml中", c)
return
}
path := strings.ReplaceAll(global.GVA_CONFIG.AutoCode.AiPath, "{FUNC}", fmt.Sprintf("api/chat/%s", llm["mode"]))
res, err := request.HttpRequest(
path,
"POST",
nil,
nil,
llm,
)
if err != nil {
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
response.FailWithMessage("大模型生成失败"+err.Error(), c)
return
}
var resStruct response.Response
b, err := io.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
response.FailWithMessage("大模型生成失败"+err.Error(), c)
return
}
err = json.Unmarshal(b, &resStruct)
if err != nil {
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
response.FailWithMessage("大模型生成失败"+err.Error(), c)
return
}
if resStruct.Code == 7 {
global.GVA_LOG.Error("大模型生成失败!"+resStruct.Msg, zap.Error(err))
response.FailWithMessage("大模型生成失败"+resStruct.Msg, c)
return
}
response.OkWithData(resStruct.Data, c)
}
+70
View File
@@ -0,0 +1,70 @@
package system
import (
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"go.uber.org/zap"
)
// 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码
// var store = captcha.NewDefaultRedisStore()
var store = base64Captcha.DefaultMemStore
type BaseApi struct{}
// Captcha
// @Tags Base
// @Summary 生成验证码
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
// @Router /base/captcha [post]
func (b *BaseApi) Captcha(c *gin.Context) {
// 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
key := c.ClientIP()
v, ok := global.BlackCache.Get(key)
if !ok {
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
}
var oc bool
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
oc = true
}
// 字符,公式,验证码配置
// 生成默认数字的driver
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis
cp := base64Captcha.NewCaptcha(driver, store)
id, b64s, _, err := cp.Generate()
if err != nil {
global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
response.FailWithMessage("验证码获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysCaptchaResponse{
CaptchaId: id,
PicPath: b64s,
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
OpenCaptcha: oc,
}, "验证码获取成功", c)
}
// 类型转换
func interfaceToInt(v interface{}) (i int) {
switch v := v.(type) {
case int:
i = v
default:
i = 0
}
return
}
+69
View File
@@ -0,0 +1,69 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type CasbinApi struct{}
// UpdateCasbin
// @Tags Casbin
// @Summary 更新角色api权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
// @Success 200 {object} response.Response{msg=string} "更新角色api权限"
// @Router /casbin/UpdateCasbin [post]
func (cas *CasbinApi) UpdateCasbin(c *gin.Context) {
var cmr request.CasbinInReceive
err := c.ShouldBindJSON(&cmr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(cmr, utils.AuthorityIdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
adminAuthorityID := utils.GetUserAuthorityId(c)
err = casbinService.UpdateCasbin(adminAuthorityID, cmr.AuthorityId, cmr.CasbinInfos)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// GetPolicyPathByAuthorityId
// @Tags Casbin
// @Summary 获取权限列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
// @Success 200 {object} response.Response{data=systemRes.PolicyPathResponse,msg=string} "获取权限列表,返回包括casbin详情列表"
// @Router /casbin/getPolicyPathByAuthorityId [post]
func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) {
var casbin request.CasbinInReceive
err := c.ShouldBindJSON(&casbin)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(casbin, utils.AuthorityIdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId)
response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c)
}
@@ -0,0 +1,129 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type DictionaryApi struct{}
// CreateSysDictionary
// @Tags SysDictionary
// @Summary 创建SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionary true "SysDictionary模型"
// @Success 200 {object} response.Response{msg=string} "创建SysDictionary"
// @Router /sysDictionary/createSysDictionary [post]
func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) {
var dictionary system.SysDictionary
err := c.ShouldBindJSON(&dictionary)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryService.CreateSysDictionary(dictionary)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysDictionary
// @Tags SysDictionary
// @Summary 删除SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionary true "SysDictionary模型"
// @Success 200 {object} response.Response{msg=string} "删除SysDictionary"
// @Router /sysDictionary/deleteSysDictionary [delete]
func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
var dictionary system.SysDictionary
err := c.ShouldBindJSON(&dictionary)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryService.DeleteSysDictionary(dictionary)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateSysDictionary
// @Tags SysDictionary
// @Summary 更新SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionary true "SysDictionary模型"
// @Success 200 {object} response.Response{msg=string} "更新SysDictionary"
// @Router /sysDictionary/updateSysDictionary [put]
func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
var dictionary system.SysDictionary
err := c.ShouldBindJSON(&dictionary)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryService.UpdateSysDictionary(&dictionary)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysDictionary
// @Tags SysDictionary
// @Summary 用id查询SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysDictionary true "ID或字典英名"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionary"
// @Router /sysDictionary/findSysDictionary [get]
func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
var dictionary system.SysDictionary
err := c.ShouldBindQuery(&dictionary)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status)
if err != nil {
global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err))
response.FailWithMessage("字典未创建或未开启", c)
return
}
response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
}
// GetSysDictionaryList
// @Tags SysDictionary
// @Summary 分页获取SysDictionary列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
// @Router /sysDictionary/getSysDictionaryList [get]
func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
list, err := dictionaryService.GetSysDictionaryInfoList()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(list, "获取成功", c)
}
@@ -0,0 +1,148 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type DictionaryDetailApi struct{}
// CreateSysDictionaryDetail
// @Tags SysDictionaryDetail
// @Summary 创建SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
// @Success 200 {object} response.Response{msg=string} "创建SysDictionaryDetail"
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
var detail system.SysDictionaryDetail
err := c.ShouldBindJSON(&detail)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryDetailService.CreateSysDictionaryDetail(detail)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysDictionaryDetail
// @Tags SysDictionaryDetail
// @Summary 删除SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
// @Success 200 {object} response.Response{msg=string} "删除SysDictionaryDetail"
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
var detail system.SysDictionaryDetail
err := c.ShouldBindJSON(&detail)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryDetailService.DeleteSysDictionaryDetail(detail)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateSysDictionaryDetail
// @Tags SysDictionaryDetail
// @Summary 更新SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail"
// @Success 200 {object} response.Response{msg=string} "更新SysDictionaryDetail"
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
var detail system.SysDictionaryDetail
err := c.ShouldBindJSON(&detail)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = dictionaryDetailService.UpdateSysDictionaryDetail(&detail)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysDictionaryDetail
// @Tags SysDictionaryDetail
// @Summary 用id查询SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionaryDetail"
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
var detail system.SysDictionaryDetail
err := c.ShouldBindQuery(&detail)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(detail, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
return
}
response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c)
}
// GetSysDictionaryDetailList
// @Tags SysDictionaryDetail
// @Summary 分页获取SysDictionaryDetail列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量"
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
var pageInfo request.SysDictionaryDetailSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
@@ -0,0 +1,258 @@
package system
import (
"fmt"
"net/http"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SysExportTemplateApi struct {
}
var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService
// CreateSysExportTemplate 创建导出模板
// @Tags SysExportTemplate
// @Summary 创建导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "创建导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /sysExportTemplate/createSysExportTemplate [post]
func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
verify := utils.Rules{
"Name": {utils.NotEmpty()},
}
if err := utils.Verify(sysExportTemplate, verify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
} else {
response.OkWithMessage("创建成功", c)
}
}
// DeleteSysExportTemplate 删除导出模板
// @Tags SysExportTemplate
// @Summary 删除导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "删除导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
} else {
response.OkWithMessage("删除成功", c)
}
}
// DeleteSysExportTemplateByIds 批量删除导出模板
// @Tags SysExportTemplate
// @Summary 批量删除导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete]
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) {
var IDS request.IdsReq
err := c.ShouldBindJSON(&IDS)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
} else {
response.OkWithMessage("批量删除成功", c)
}
}
// UpdateSysExportTemplate 更新导出模板
// @Tags SysExportTemplate
// @Summary 更新导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysExportTemplate true "更新导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /sysExportTemplate/updateSysExportTemplate [put]
func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindJSON(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
verify := utils.Rules{
"Name": {utils.NotEmpty()},
}
if err := utils.Verify(sysExportTemplate, verify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
} else {
response.OkWithMessage("更新成功", c)
}
}
// FindSysExportTemplate 用id查询导出模板
// @Tags SysExportTemplate
// @Summary 用id查询导出模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysExportTemplate true "用id查询导出模板"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /sysExportTemplate/findSysExportTemplate [get]
func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) {
var sysExportTemplate system.SysExportTemplate
err := c.ShouldBindQuery(&sysExportTemplate)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
} else {
response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c)
}
}
// GetSysExportTemplateList 分页获取导出模板列表
// @Tags SysExportTemplate
// @Summary 分页获取导出模板列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysExportTemplate/getSysExportTemplateList [get]
func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) {
var pageInfo systemReq.SysExportTemplateSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
}
// ExportExcel 导出表格
// @Tags SysExportTemplate
// @Summary 导出表格
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcel [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
templateID := c.Query("templateID")
queryParams := c.Request.URL.Query()
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx")) // 对下载的文件重命名
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
}
// ExportTemplate 导出表格模板
// @Tags SysExportTemplate
// @Summary 导出表格模板
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/exportExcel [get]
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
} else {
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx")) // 对下载的文件重命名
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
}
}
// ImportExcel 导入表格
// @Tags SysImportTemplate
// @Summary 导入表格
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /sysExportTemplate/importExcel [post]
func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
file, err := c.FormFile("file")
if err != nil {
global.GVA_LOG.Error("文件获取失败!", zap.Error(err))
response.FailWithMessage("文件获取失败", c)
return
}
if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil {
global.GVA_LOG.Error(err.Error(), zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
response.OkWithMessage("导入成功", c)
}
}
+66
View File
@@ -0,0 +1,66 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"go.uber.org/zap"
"github.com/gin-gonic/gin"
)
type DBApi struct{}
// InitDB
// @Tags InitDB
// @Summary 初始化用户数据库
// @Produce application/json
// @Param data body request.InitDB true "初始化数据库参数"
// @Success 200 {object} response.Response{data=string} "初始化用户数据库"
// @Router /init/initdb [post]
func (i *DBApi) InitDB(c *gin.Context) {
if global.GVA_DB != nil {
global.GVA_LOG.Error("已存在数据库配置!")
response.FailWithMessage("已存在数据库配置", c)
return
}
var dbInfo request.InitDB
if err := c.ShouldBindJSON(&dbInfo); err != nil {
global.GVA_LOG.Error("参数校验不通过!", zap.Error(err))
response.FailWithMessage("参数校验不通过", c)
return
}
if err := initDBService.InitDB(dbInfo); err != nil {
global.GVA_LOG.Error("自动创建数据库失败!", zap.Error(err))
response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c)
return
}
// 二开部分:新增同步管理员账号
user := system.UserExtendService{}
go user.SyncUser()
response.OkWithMessage("自动创建数据库成功", c)
}
// CheckDB
// @Tags CheckDB
// @Summary 初始化用户数据库
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "初始化用户数据库"
// @Router /init/checkdb [post]
func (i *DBApi) CheckDB(c *gin.Context) {
var (
message = "前往初始化数据库"
needInit = true
)
if global.GVA_DB != nil {
message = "数据库无需初始化"
needInit = false
}
global.GVA_LOG.Info(message)
response.OkWithDetailed(gin.H{"needInit": needInit}, message, c)
}
@@ -0,0 +1,33 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type JwtApi struct{}
// JsonInBlacklist
// @Tags Jwt
// @Summary jwt加入黑名单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单"
// @Router /jwt/jsonInBlacklist [post]
func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
token := utils.GetToken(c)
jwt := system.JwtBlacklist{Jwt: token}
err := jwtService.JsonInBlacklist(jwt)
if err != nil {
global.GVA_LOG.Error("jwt作废失败!", zap.Error(err))
response.FailWithMessage("jwt作废失败", c)
return
}
utils.ClearToken(c)
response.OkWithMessage("jwt作废成功", c)
}
+265
View File
@@ -0,0 +1,265 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type AuthorityMenuApi struct{}
// GetMenu
// @Tags AuthorityMenu
// @Summary 获取用户动态路由
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body request.Empty true "空"
// @Success 200 {object} response.Response{data=systemRes.SysMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表"
// @Router /menu/getMenu [post]
func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c))
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
if menus == nil {
menus = []system.SysMenu{}
}
response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
}
// GetBaseMenuTree
// @Tags AuthorityMenu
// @Summary 获取用户动态路由
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body request.Empty true "空"
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单列表"
// @Router /menu/getBaseMenuTree [post]
func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) {
authority := utils.GetUserAuthorityId(c)
menus, err := menuService.GetBaseMenuTree(authority)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
}
// AddMenuAuthority
// @Tags AuthorityMenu
// @Summary 增加menu和角色关联关系
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID"
// @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系"
// @Router /menu/addMenuAuthority [post]
func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
var authorityMenu systemReq.AddMenuAuthorityInfo
err := c.ShouldBindJSON(&authorityMenu)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
adminAuthorityID := utils.GetUserAuthorityId(c)
if err := menuService.AddMenuAuthority(authorityMenu.Menus, adminAuthorityID, authorityMenu.AuthorityId); err != nil {
global.GVA_LOG.Error("添加失败!", zap.Error(err))
response.FailWithMessage("添加失败", c)
} else {
response.OkWithMessage("添加成功", c)
}
}
// GetMenuAuthority
// @Tags AuthorityMenu
// @Summary 获取指定角色menu
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetAuthorityId true "角色ID"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取指定角色menu"
// @Router /menu/getMenuAuthority [post]
func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) {
var param request.GetAuthorityId
err := c.ShouldBindJSON(&param)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(param, utils.AuthorityIdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
menus, err := menuService.GetMenuAuthority(&param)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c)
return
}
response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
}
// AddBaseMenu
// @Tags Menu
// @Summary 新增菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
// @Success 200 {object} response.Response{msg=string} "新增菜单"
// @Router /menu/addBaseMenu [post]
func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
var menu system.SysBaseMenu
err := c.ShouldBindJSON(&menu)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(menu, utils.MenuVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = menuService.AddBaseMenu(menu)
if err != nil {
global.GVA_LOG.Error("添加失败!", zap.Error(err))
response.FailWithMessage("添加失败", c)
return
}
response.OkWithMessage("添加成功", c)
}
// DeleteBaseMenu
// @Tags Menu
// @Summary 删除菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "菜单id"
// @Success 200 {object} response.Response{msg=string} "删除菜单"
// @Router /menu/deleteBaseMenu [post]
func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
var menu request.GetById
err := c.ShouldBindJSON(&menu)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(menu, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = baseMenuService.DeleteBaseMenu(menu.ID)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateBaseMenu
// @Tags Menu
// @Summary 更新菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
// @Success 200 {object} response.Response{msg=string} "更新菜单"
// @Router /menu/updateBaseMenu [post]
func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) {
var menu system.SysBaseMenu
err := c.ShouldBindJSON(&menu)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(menu, utils.MenuVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = baseMenuService.UpdateBaseMenu(menu)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// GetBaseMenuById
// @Tags Menu
// @Summary 根据id获取菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "菜单id"
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenuResponse,msg=string} "根据id获取菜单,返回包括系统菜单列表"
// @Router /menu/getBaseMenuById [post]
func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
var idInfo request.GetById
err := c.ShouldBindJSON(&idInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(idInfo, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
menu, err := baseMenuService.GetBaseMenuById(idInfo.ID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
}
// GetMenuList
// @Tags Menu
// @Summary 分页获取基础menu列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "页码, 每页大小"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取基础menu列表,返回包括列表,总数,页码,每页数量"
// @Router /menu/getMenuList [post]
func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) {
authorityID := utils.GetUserAuthorityId(c)
menuList, err := menuService.GetInfoList(authorityID)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(menuList, "获取成功", c)
}
@@ -0,0 +1,149 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type OperationRecordApi struct{}
// CreateSysOperationRecord
// @Tags SysOperationRecord
// @Summary 创建SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysOperationRecord true "创建SysOperationRecord"
// @Success 200 {object} response.Response{msg=string} "创建SysOperationRecord"
// @Router /sysOperationRecord/createSysOperationRecord [post]
func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) {
var sysOperationRecord system.SysOperationRecord
err := c.ShouldBindJSON(&sysOperationRecord)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = operationRecordService.CreateSysOperationRecord(sysOperationRecord)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysOperationRecord
// @Tags SysOperationRecord
// @Summary 删除SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysOperationRecord true "SysOperationRecord模型"
// @Success 200 {object} response.Response{msg=string} "删除SysOperationRecord"
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) {
var sysOperationRecord system.SysOperationRecord
err := c.ShouldBindJSON(&sysOperationRecord)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = operationRecordService.DeleteSysOperationRecord(sysOperationRecord)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysOperationRecordByIds
// @Tags SysOperationRecord
// @Summary 批量删除SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除SysOperationRecord"
// @Success 200 {object} response.Response{msg=string} "批量删除SysOperationRecord"
// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete]
func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
var IDS request.IdsReq
err := c.ShouldBindJSON(&IDS)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = operationRecordService.DeleteSysOperationRecordByIds(IDS)
if err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// FindSysOperationRecord
// @Tags SysOperationRecord
// @Summary 用id查询SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysOperationRecord true "Id"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysOperationRecord"
// @Router /sysOperationRecord/findSysOperationRecord [get]
func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
var sysOperationRecord system.SysOperationRecord
err := c.ShouldBindQuery(&sysOperationRecord)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(sysOperationRecord, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
return
}
response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c)
}
// GetSysOperationRecordList
// @Tags SysOperationRecord
// @Summary 分页获取SysOperationRecord列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量"
// @Router /sysOperationRecord/getSysOperationRecordList [get]
func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) {
var pageInfo systemReq.SysOperationRecordSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
+171
View File
@@ -0,0 +1,171 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SysParamsApi struct{}
// CreateSysParams 创建参数
// @Tags SysParams
// @Summary 创建参数
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysParams true "创建参数"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /sysParams/createSysParams [post]
func (sysParamsApi *SysParamsApi) CreateSysParams(c *gin.Context) {
var sysParams system.SysParams
err := c.ShouldBindJSON(&sysParams)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = sysParamsService.CreateSysParams(&sysParams)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysParams 删除参数
// @Tags SysParams
// @Summary 删除参数
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysParams true "删除参数"
// @Success 200 {object} response.Response{msg=string} "删除成功"
// @Router /sysParams/deleteSysParams [delete]
func (sysParamsApi *SysParamsApi) DeleteSysParams(c *gin.Context) {
ID := c.Query("ID")
err := sysParamsService.DeleteSysParams(ID)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysParamsByIds 批量删除参数
// @Tags SysParams
// @Summary 批量删除参数
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
// @Router /sysParams/deleteSysParamsByIds [delete]
func (sysParamsApi *SysParamsApi) DeleteSysParamsByIds(c *gin.Context) {
IDs := c.QueryArray("IDs[]")
err := sysParamsService.DeleteSysParamsByIds(IDs)
if err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// UpdateSysParams 更新参数
// @Tags SysParams
// @Summary 更新参数
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysParams true "更新参数"
// @Success 200 {object} response.Response{msg=string} "更新成功"
// @Router /sysParams/updateSysParams [put]
func (sysParamsApi *SysParamsApi) UpdateSysParams(c *gin.Context) {
var sysParams system.SysParams
err := c.ShouldBindJSON(&sysParams)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = sysParamsService.UpdateSysParams(sysParams)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysParams 用id查询参数
// @Tags SysParams
// @Summary 用id查询参数
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query system.SysParams true "用id查询参数"
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "查询成功"
// @Router /sysParams/findSysParams [get]
func (sysParamsApi *SysParamsApi) FindSysParams(c *gin.Context) {
ID := c.Query("ID")
resysParams, err := sysParamsService.GetSysParams(ID)
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(resysParams, c)
}
// GetSysParamsList 分页获取参数列表
// @Tags SysParams
// @Summary 分页获取参数列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query systemReq.SysParamsSearch true "分页获取参数列表"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
// @Router /sysParams/getSysParamsList [get]
func (sysParamsApi *SysParamsApi) GetSysParamsList(c *gin.Context) {
var pageInfo systemReq.SysParamsSearch
err := c.ShouldBindQuery(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := sysParamsService.GetSysParamsInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// GetSysParam 根据key获取参数value
// @Tags SysParams
// @Summary 根据key获取参数value
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param key query string true "key"
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "获取成功"
// @Router /sysParams/getSysParam [get]
func (sysParamsApi *SysParamsApi) GetSysParam(c *gin.Context) {
k := c.Query("key")
params, err := sysParamsService.GetSysParam(k)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(params, "获取成功", c)
}
+88
View File
@@ -0,0 +1,88 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SystemApi struct{}
// GetSystemConfig
// @Tags System
// @Summary 获取配置文件内容
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {object} response.Response{data=systemRes.SysConfigResponse,msg=string} "获取配置文件内容,返回包括系统配置"
// @Router /system/getSystemConfig [post]
func (s *SystemApi) GetSystemConfig(c *gin.Context) {
config, err := systemConfigService.GetSystemConfig()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c)
}
// SetSystemConfig
// @Tags System
// @Summary 设置配置文件内容
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body system.System true "设置配置文件内容"
// @Success 200 {object} response.Response{data=string} "设置配置文件内容"
// @Router /system/setSystemConfig [post]
func (s *SystemApi) SetSystemConfig(c *gin.Context) {
var sys system.System
err := c.ShouldBindJSON(&sys)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = systemConfigService.SetSystemConfig(sys)
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
return
}
response.OkWithMessage("设置成功", c)
}
// ReloadSystem
// @Tags System
// @Summary 重启系统
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "重启系统"
// @Router /system/reloadSystem [post]
func (s *SystemApi) ReloadSystem(c *gin.Context) {
err := utils.Reload()
if err != nil {
global.GVA_LOG.Error("重启系统失败!", zap.Error(err))
response.FailWithMessage("重启系统失败", c)
return
}
response.OkWithMessage("重启系统成功", c)
}
// GetServerInfo
// @Tags System
// @Summary 获取服务器信息
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取服务器信息"
// @Router /system/getServerInfo [post]
func (s *SystemApi) GetServerInfo(c *gin.Context) {
server, err := systemConfigService.GetServerInfo()
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"server": server}, "获取成功", c)
}
+503
View File
@@ -0,0 +1,503 @@
package system
import (
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
"github.com/flipped-aurora/gin-vue-admin/server/service/gaia"
"strconv"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
// Login
// @Tags Base
// @Summary 用户登录
// @Produce application/json
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
// @Router /base/login [post]
func (b *BaseApi) Login(c *gin.Context) {
var l systemReq.Login
err := c.ShouldBindJSON(&l)
key := c.ClientIP()
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(l, utils.LoginVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 判断验证码是否开启
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
v, ok := global.BlackCache.Get(key)
if !ok {
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
}
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
if !oc || (l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true)) {
u := &system.SysUser{Username: l.Username, Password: l.Password}
user, err := userService.Login(u)
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户名不存在或者密码错误", c)
return
}
if user.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
return
}
// 验证码次数+1
global.BlackCache.Increment(key, 1)
response.FailWithMessage("验证码错误", c)
}
// TokenNext 登录以后签发jwt
func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
token, claims, err := utils.LoginToken(&user)
if err != nil {
global.GVA_LOG.Error("获取token失败!", zap.Error(err))
response.FailWithMessage("获取token失败", c)
return
}
if !global.GVA_CONFIG.System.UseMultipoint {
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
return
}
if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
response.FailWithMessage("设置登录状态失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
} else if err != nil {
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
response.FailWithMessage("设置登录状态失败", c)
} else {
var blackJWT system.JwtBlacklist
blackJWT.Jwt = jwtStr
if err := jwtService.JsonInBlacklist(blackJWT); err != nil {
response.FailWithMessage("jwt作废失败", c)
return
}
if err := jwtService.SetRedisJWT(token, user.GetUsername()); err != nil {
response.FailWithMessage("设置登录状态失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(systemRes.LoginResponse{
User: user,
Token: token,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
}
}
// Register
// @Tags SysUser
// @Summary 用户注册账号
// @Produce application/json
// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID"
// @Success 200 {object} response.Response{data=systemRes.SysUserResponse,msg=string} "用户注册账号,返回包括用户信息"
// @Router /user/admin_register [post]
func (b *BaseApi) Register(c *gin.Context) {
var r systemReq.Register
err := c.ShouldBindJSON(&r)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(r, utils.RegisterVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err = gaia.IsUserPasswordValid(r.Password); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// Extend: Start admin init
r.AuthorityId = system.AdminGroupID
// Extend: stop admin init
var authorities []system.SysAuthority
for _, v := range r.AuthorityIds {
authorities = append(authorities, system.SysAuthority{
AuthorityId: v,
})
}
token := utils.GetToken(c)
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable, Phone: r.Phone, Email: r.Email}
userReturn, err := userService.Register(*user, token)
if err != nil {
global.GVA_LOG.Error("注册失败!", zap.Error(err))
response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
return
}
response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
}
// ChangePassword
// @Tags SysUser
// @Summary 用户修改密码
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码"
// @Success 200 {object} response.Response{msg=string} "用户修改密码"
// @Router /user/changePassword [post]
func (b *BaseApi) ChangePassword(c *gin.Context) {
var req systemReq.ChangePasswordReq
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(req, utils.ChangePasswordVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
uid := utils.GetUserID(c)
if err = gaia.IsUserPasswordValid(req.Password); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
_, err = userService.ChangePassword(u, req.NewPassword)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
return
}
response.OkWithMessage("修改成功", c)
}
// GetUserList
// @Tags SysUser
// @Summary 分页获取用户列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.GetUserList true "页码, 每页大小"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取用户列表,返回包括列表,总数,页码,每页数量"
// @Router /user/getUserList [post]
func (b *BaseApi) GetUserList(c *gin.Context) {
var pageInfo systemReq.GetUserList
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(pageInfo, utils.PageInfoVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := userService.GetUserInfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Page,
PageSize: pageInfo.PageSize,
}, "获取成功", c)
}
// SetUserAuthority
// @Tags SysUser
// @Summary 更改用户权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID"
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
// @Router /user/setUserAuthority [post]
func (b *BaseApi) SetUserAuthority(c *gin.Context) {
var sua systemReq.SetUserAuth
err := c.ShouldBindJSON(&sua)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil {
response.FailWithMessage(UserVerifyErr.Error(), c)
return
}
userID := utils.GetUserID(c)
err = userService.SetUserAuthority(userID, sua.AuthorityId)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
return
}
claims := utils.GetUserInfo(c)
j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
claims.AuthorityId = sua.AuthorityId
if token, err := j.CreateToken(*claims); err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage(err.Error(), c)
} else {
c.Header("new-token", token)
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
utils.SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
response.OkWithMessage("修改成功", c)
}
}
// SetUserAuthorities
// @Tags SysUser
// @Summary 设置用户权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID"
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
// @Router /user/setUserAuthorities [post]
func (b *BaseApi) SetUserAuthorities(c *gin.Context) {
var sua systemReq.SetUserAuthorities
err := c.ShouldBindJSON(&sua)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
authorityID := utils.GetUserAuthorityId(c)
err = userService.SetUserAuthorities(authorityID, sua.ID, sua.AuthorityIds)
if err != nil {
global.GVA_LOG.Error("修改失败!", zap.Error(err))
response.FailWithMessage("修改失败", c)
return
}
response.OkWithMessage("修改成功", c)
}
// DeleteUser
// @Tags SysUser
// @Summary 删除用户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.GetById true "用户ID"
// @Success 200 {object} response.Response{msg=string} "删除用户"
// @Router /user/deleteUser [delete]
func (b *BaseApi) DeleteUser(c *gin.Context) {
var reqId request.GetById
err := c.ShouldBindJSON(&reqId)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(reqId, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
jwtId := utils.GetUserID(c)
if jwtId == uint(reqId.ID) {
response.FailWithMessage("删除失败, 无法删除自己。", c)
return
}
err = userService.DeleteUser(reqId.ID)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// SetUserInfo
// @Tags SysUser
// @Summary 设置用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
// @Router /user/setUserInfo [put]
func (b *BaseApi) SetUserInfo(c *gin.Context) {
var user systemReq.ChangeUserInfo
err := c.ShouldBindJSON(&user)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(user, utils.IdVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if len(user.AuthorityIds) != 0 {
authorityID := utils.GetUserAuthorityId(c)
err = userService.SetUserAuthorities(authorityID, user.ID, user.AuthorityIds)
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
return
}
}
err = userService.SetUserInfo(system.SysUser{
GVA_MODEL: global.GVA_MODEL{
ID: user.ID,
},
NickName: user.NickName,
HeaderImg: user.HeaderImg,
Phone: user.Phone,
Email: user.Email,
Enable: user.Enable,
}, user.GlobalCode)
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
return
}
response.OkWithMessage("设置成功", c)
}
// SetSelfInfo
// @Tags SysUser
// @Summary 设置用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
// @Router /user/SetSelfInfo [put]
func (b *BaseApi) SetSelfInfo(c *gin.Context) {
var user systemReq.ChangeUserInfo
err := c.ShouldBindJSON(&user)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
user.ID = utils.GetUserID(c)
err = userService.SetSelfInfo(system.SysUser{
GVA_MODEL: global.GVA_MODEL{
ID: user.ID,
},
NickName: user.NickName,
HeaderImg: user.HeaderImg,
Phone: user.Phone,
Email: user.Email,
Enable: user.Enable,
})
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
return
}
response.OkWithMessage("设置成功", c)
}
// SetSelfSetting
// @Tags SysUser
// @Summary 设置用户配置
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body map[string]interface{} true "用户配置数据"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户配置"
// @Router /user/SetSelfSetting [put]
func (b *BaseApi) SetSelfSetting(c *gin.Context) {
var req common.JSONMap
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = userService.SetSelfSetting(req, utils.GetUserID(c))
if err != nil {
global.GVA_LOG.Error("设置失败!", zap.Error(err))
response.FailWithMessage("设置失败", c)
return
}
response.OkWithMessage("设置成功", c)
}
// GetUserInfo
// @Tags SysUser
// @Summary 获取用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取用户信息"
// @Router /user/getUserInfo [get]
func (b *BaseApi) GetUserInfo(c *gin.Context) {
uuid := utils.GetUserUuid(c)
ReqUser, err := userService.GetUserInfo(uuid)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
}
// ResetPassword
// @Tags SysUser
// @Summary 重置用户密码
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body system.SysUser true "ID"
// @Success 200 {object} response.Response{msg=string} "重置用户密码"
// @Router /user/resetPassword [post]
func (b *BaseApi) ResetPassword(c *gin.Context) {
// Extend Start: update password
var user systemReq.UpdateUserPasswd
err := c.ShouldBindJSON(&user)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err = gaia.IsUserPasswordValid(user.Password); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = userService.ResetPassword(user.ID, user.Password)
// Extend Stop: update password
if err != nil {
global.GVA_LOG.Error("重置失败!", zap.Error(err))
response.FailWithMessage("重置失败"+err.Error(), c)
return
}
response.OkWithMessage("重置成功", c)
}
@@ -0,0 +1,158 @@
package system
import (
"encoding/json"
"errors"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"go.uber.org/zap"
"gorm.io/gorm"
)
// Extend Start: sync user
// SyncUser
// @Tags Base
// @Summary 用户同步
// @Produce application/json
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
// @Router /user/sync [post]
func (b *BaseApi) SyncUser(c *gin.Context) {
userExtendService.SyncUser()
response.OkWithMessage("同步中", c)
}
// Extend Stop: sync user
// OaLogin
// @Tags Base
// @Summary 用户登录
// @Produce application/json
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
// @Router /base/oaLogin [post]
func (b *BaseApi) OaLogin(c *gin.Context) {
var l systemReq.OaLoginReq
err := c.ShouldBindJSON(&l)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err = utils.Verify(l, utils.OaLoginVerify)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
/**
请求获取accessToken
*/
clientGetToken := resty.New().R()
var accessTokenResponse *resty.Response
getTokenUrl := fmt.Sprintf("%s%s", global.GVA_CONFIG.OaLogin.Url, global.GVA_CONFIG.OaLogin.GetTokenByCodeApiPath)
var postParams = map[string]string{
"client_id": global.GVA_CONFIG.OaLogin.Oauth2ClientId,
"client_secret": global.GVA_CONFIG.OaLogin.Oauth2ClientSecret,
"code": l.AuthorizeCode,
"grant_type": "authorization_code",
"redirect_uri": "",
}
accessTokenResponse, err = clientGetToken.
SetFormData(postParams).
Post(getTokenUrl)
if err != nil {
global.GVA_LOG.Error("请求OA用户信息失败,响应数据为:", zap.Error(errors.New(accessTokenResponse.String())))
response.FailWithMessage("请求OA用户信息失败:"+err.Error(), c)
return
}
tokenRes := accessTokenResponse.String()
var oaAccessToken systemRes.OaAccessTokenRes
err = json.Unmarshal([]byte(tokenRes), &oaAccessToken)
if err != nil {
global.GVA_LOG.Error("解析OA AccessToken接口返回数据失败,响应数据为:", zap.Error(errors.New(accessTokenResponse.String())))
response.FailWithMessage("解析OA AccessToken接口返回数据失败:"+err.Error(), c)
return
}
/**
请求OA返回用户信息
*/
getUserInfoUrl := fmt.Sprintf("%s%s", global.GVA_CONFIG.OaLogin.Url, global.GVA_CONFIG.OaLogin.GetUserApiPath)
clientGetUser := resty.New().R()
var userInfoResponse *resty.Response
userInfoResponse, err = clientGetUser.SetHeader("Authorization", oaAccessToken.AccessToken).Post(getUserInfoUrl)
if err != nil {
global.GVA_LOG.Error("请求OA用户信息失败,响应数据为:", zap.Error(errors.New(userInfoResponse.String())))
response.FailWithMessage("请求OA用户信息失败:"+err.Error(), c)
return
}
userInfoRes := userInfoResponse.String()
var oaUserInfo systemRes.OaUserInfoRes
err = json.Unmarshal([]byte(userInfoRes), &oaUserInfo)
if err != nil {
global.GVA_LOG.Error("解析OA用户信息接口返回数据失败,响应数据为:", zap.Error(errors.New(userInfoRes)))
global.GVA_LOG.Error("解析OA用户信息接口返回数据失败,请求Token为:", zap.String("", oaAccessToken.AccessToken))
global.GVA_LOG.Error("解析OA用户信息接口返回数据失败,请求Token为:", zap.String("", accessTokenResponse.String()))
response.FailWithMessage("解析OA用户信息接口返回数据失败:"+err.Error(), c)
return
}
// 查询数据库数据
sysUser := &system.SysUser{}
err = global.GVA_DB.Where("email", oaUserInfo.Data.Email).First(&sysUser).Error
if err != nil && err != gorm.ErrRecordNotFound {
response.FailWithMessage("查询数据库信息失败:"+err.Error(), c)
return
}
// 判断是否需要注册
if sysUser.ID == 0 {
// TODO 从ldap中获取用户详细数据
// 注册用户
sysUser = &system.SysUser{
Username: oaUserInfo.Data.Username,
NickName: oaUserInfo.Data.Username,
HeaderImg: "https://hn1.oss-cn-shenzhen.aliyuncs.com/w.jpg",
AuthorityId: system.NormalAuthorityId,
Authorities: []system.SysAuthority{{AuthorityId: system.NormalAuthorityId}},
Enable: 1,
//Phone: r.Phone, // TODO 手机需要从ldap中获取用户详细数据
Email: oaUserInfo.Data.Email,
Password: utils.RandomString(16),
}
var userReturn system.SysUser
userReturn, err = userService.Register(*sysUser, "")
if err != nil {
global.GVA_LOG.Error("注册失败!", zap.Error(err))
response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
return
} else {
global.GVA_LOG.Info("注册成功!", zap.Any("username", userReturn.Username))
}
sysUser = &userReturn
}
var user *system.SysUser
user, err = userExtendService.OaLogin(sysUser) // 注意这个方法不检查密码
if err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在!", zap.Error(err))
response.FailWithMessage("用户名不存在", c)
return
}
if sysUser.Enable != 1 {
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
response.FailWithMessage("用户被禁止登录", c)
return
}
b.TokenNext(c, *user)
return
}
+173
View File
@@ -0,0 +1,173 @@
aliyun-oss:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
autocode:
web: web/src
root:
server: server
module: github.com/flipped-aurora/gin-vue-admin/server
ai-path: ""
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ""
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
s3-force-path-style: false
disable-ssl: false
captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0
open-captcha-timeout: 3600
cloudflare-r2:
bucket: xxxx0bucket
base-url: https://gin.vue.admin.com
path: uploads
account-id: xxx_account_id
access-key-id: xxx_key_id
secret-access-key: xxx_secret_key
cors:
mode: strict-whitelist
whitelist:
- allow-origin: example1.com
allow-methods: POST, GET
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true
- allow-origin: example2.com
allow-methods: GET, POST
allow-headers: content-type
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true
db-list:
- type: ""
alias-name: ""
prefix: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
path: ""
engine: ""
log-mode: ""
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
disable: true
disk-list:
- mount-point: /
email:
to: xxx@qq.com
from: xxx@163.com
host: smtp.163.com
secret: xxx
nickname: test
port: 465
is-ssl: true
excel:
dir: ./resource/excel/
gaia:
url: http://api:5001
login_max_error_limit: 5
SUPER_ADMIN_ACCOUNT_ID:
SUPER_ADMIN_TENANT_ID:
hua-wei-obs:
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
jwt:
signing-key: sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U
expires-time: 1d
buffer-time: 1d
issuer: CLOUD
local:
path: uploads/file
store-path: uploads/file
minio:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
use-ssl: false
base-path: ""
bucket-url: http://host:9000/yourBucketName
oa-login:
url:
oauth2-client-id:
oauth2-client-secret:
get-user-info-api-path:
get-token-by-code-api-path:
pgsql:
prefix: ""
port: "5432"
config: sslmode=disable TimeZone=Asia/Shanghai
db-name:
username:
password:
path:
engine: ""
log-mode: error
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
qiniu:
zone: ZoneHuaDong
bucket: ""
img-path: ""
access-key: ""
secret-key: ""
use-https: false
use-cdn-domains: false
redis:
name: ""
addr: redis:6379
password: difyai123456
db: 8
useCluster: false
clusterAddrs:
- 172.21.0.3:7000
- 172.21.0.4:7001
- 172.21.0.2:7002
system:
db-type: pgsql
oss-type: local
router-prefix: ""
addr: 8888
iplimit-count: 15000
iplimit-time: 3600
use-multipoint: false
use-redis: true
use-mongo: false
use-strict-auth: false
user_default-group-id: "888"
docker-run: true
tencent-cos:
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
zap:
level: info
prefix: '[dify_plus/admin_server]'
format: json
director: log
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
show-line: true
log-in-console: true
retention-day: -1
+71
View File
@@ -0,0 +1,71 @@
gaia:
url: http://127.0.0.1:5001
login_max_error_limit: 5
SUPER_ADMIN_ACCOUNT_ID:
SUPER_ADMIN_TENANT_ID:
captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0
open-captcha-timeout: 3600
jwt:
signing-key:
expires-time: 1d
buffer-time: 1d
issuer: CLOUD
local:
path: uploads/file
store-path: uploads/file
oa-login:
url:
oauth2-client-id:
oauth2-client-secret:
get-user-info-api-path:
get-token-by-code-api-path:
pgsql:
prefix: ""
port: "5432"
config: sslmode=disable TimeZone=Asia/Shanghai
db-name: dify_plus
username: postgres
password: difyai123456
path: 127.0.0.1
engine: ""
log-mode: error
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
redis:
name: ""
addr: 127.0.0.1:6379
password: difyai123456
db: 8
useCluster: false
clusterAddrs:
- 172.21.0.3:7000
- 172.21.0.4:7001
- 172.21.0.2:7002
system:
db-type: pgsql
oss-type: local
router-prefix: ""
addr: 8888
iplimit-count: 15000
iplimit-time: 3600
use-multipoint: false
use-redis: true
use-mongo: false
use-strict-auth: false
user_default-group-id: "888"
zap:
level: info
prefix: '[gaia/server]'
format: json
director: log
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
show-line: true
log-in-console: true
retention-day: -1
+22
View File
@@ -0,0 +1,22 @@
package config
import (
"path/filepath"
"strings"
)
type Autocode struct {
Web string `mapstructure:"web" json:"web" yaml:"web"`
Root string `mapstructure:"root" json:"root" yaml:"root"`
Server string `mapstructure:"server" json:"server" yaml:"server"`
Module string `mapstructure:"module" json:"module" yaml:"module"`
AiPath string `mapstructure:"ai-path" json:"ai-path" yaml:"ai-path"`
}
func (a *Autocode) WebRoot() string {
webs := strings.Split(a.Web, "/")
if len(webs) == 0 {
webs = strings.Split(a.Web, "\\")
}
return filepath.Join(webs...)
}
+9
View File
@@ -0,0 +1,9 @@
package config
type Captcha struct {
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒)
}
+44
View File
@@ -0,0 +1,44 @@
package config
type Server struct {
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"`
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
RedisList []Redis `mapstructure:"redis-list" json:"redis-list" yaml:"redis-list"`
DifyRedis Redis `mapstructure:"dify-redis" json:"dify-redis" yaml:"dify-redis"` // Extend: Global Code
Mongo Mongo `mapstructure:"mongo" json:"mongo" yaml:"mongo"`
Email Email `mapstructure:"email" json:"email" yaml:"email"`
System System `mapstructure:"system" json:"system" yaml:"system"`
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
// auto
AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
// gorm
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"`
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
Oracle Oracle `mapstructure:"oracle" json:"oracle" yaml:"oracle"`
Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
// oss
Local Local `mapstructure:"local" json:"local" yaml:"local"`
Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"`
HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"`
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
CloudflareR2 CloudflareR2 `mapstructure:"cloudflare-r2" json:"cloudflare-r2" yaml:"cloudflare-r2"`
Minio Minio `mapstructure:"minio" json:"minio" yaml:"minio"`
Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
DiskList []DiskList `mapstructure:"disk-list" json:"disk-list" yaml:"disk-list"`
// 跨域配置
Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
// 对接OA登录oauth2.0
OaLogin OaLogin `mapstructure:"oa-login" json:"oa-login" yaml:"oa-login"`
// 对接Gaia平台相关配置
Gaia Gaia `mapstructure:"gaia" json:"gaia" yaml:"gaia"`
}
+14
View File
@@ -0,0 +1,14 @@
package config
type CORS struct {
Mode string `mapstructure:"mode" json:"mode" yaml:"mode"`
Whitelist []CORSWhitelist `mapstructure:"whitelist" json:"whitelist" yaml:"whitelist"`
}
type CORSWhitelist struct {
AllowOrigin string `mapstructure:"allow-origin" json:"allow-origin" yaml:"allow-origin"`
AllowMethods string `mapstructure:"allow-methods" json:"allow-methods" yaml:"allow-methods"`
AllowHeaders string `mapstructure:"allow-headers" json:"allow-headers" yaml:"allow-headers"`
ExposeHeaders string `mapstructure:"expose-headers" json:"expose-headers" yaml:"expose-headers"`
AllowCredentials bool `mapstructure:"allow-credentials" json:"allow-credentials" yaml:"allow-credentials"`
}
+52
View File
@@ -0,0 +1,52 @@
package config
import (
"gorm.io/gorm/logger"
"strings"
)
type DsnProvider interface {
Dsn() string
}
// Embeded 结构体可以压平到上一层,从而保持 config 文件的结构和原来一样
// 见 playground: https://go.dev/play/p/KIcuhqEoxmY
// GeneralDB 也被 Pgsql 和 Mysql 原样使用
type GeneralDB struct {
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 数据库前缀
Port string `mapstructure:"port" json:"port" yaml:"port"` // 数据库端口
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
Dbname string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名
Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库账号
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
Path string `mapstructure:"path" json:"path" yaml:"path"` // 数据库地址
Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` // 数据库引擎,默认InnoDB
LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志
MaxIdleConns int `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
MaxOpenConns int `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` // 是否开启全局禁用复数,true表示开启
LogZap bool `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"` // 是否通过zap写入日志文件
}
func (c GeneralDB) LogLevel() logger.LogLevel {
switch strings.ToLower(c.LogMode) {
case "silent", "Silent":
return logger.Silent
case "error", "Error":
return logger.Error
case "warn", "Warn":
return logger.Warn
case "info", "Info":
return logger.Info
default:
return logger.Info
}
}
type SpecializedDB struct {
Type string `mapstructure:"type" json:"type" yaml:"type"`
AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
GeneralDB `yaml:",inline" mapstructure:",squash"`
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
}
+9
View File
@@ -0,0 +1,9 @@
package config
type Disk struct {
MountPoint string `mapstructure:"mount-point" json:"mount-point" yaml:"mount-point"`
}
type DiskList struct {
Disk `yaml:",inline" mapstructure:",squash"`
}
+11
View File
@@ -0,0 +1,11 @@
package config
type Email struct {
To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱
Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥
Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱
Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"` // 是否SSL 是否开启SSL
}
+5
View File
@@ -0,0 +1,5 @@
package config
type Excel struct {
Dir string `mapstructure:"dir" json:"dir" yaml:"dir"`
}
+8
View File
@@ -0,0 +1,8 @@
package config
type Gaia struct {
Url string `mapstructure:"url" json:"url" yaml:"url"`
LoginMaxErrorLimit int `mapstructure:"login_max_error_limit" json:"login_max_error_limit" yaml:"login_max_error_limit"`
SuperAdminAccountId string `mapstructure:"SUPER_ADMIN_ACCOUNT_ID" json:"SUPER_ADMIN_ACCOUNT_ID" yaml:"SUPER_ADMIN_ACCOUNT_ID"` // 超级管理员账号
SuperAdminTenantId string `mapstructure:"SUPER_ADMIN_TENANT_ID" json:"SUPER_ADMIN_TENANT_ID" yaml:"SUPER_ADMIN_TENANT_ID"` // 系统默认工作区
}
+10
View File
@@ -0,0 +1,10 @@
package config
type Mssql struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
// Dsn "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
func (m *Mssql) Dsn() string {
return "sqlserver://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "?database=" + m.Dbname + "&encrypt=disable"
}
+9
View File
@@ -0,0 +1,9 @@
package config
type Mysql struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (m *Mysql) Dsn() string {
return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config
}
+10
View File
@@ -0,0 +1,10 @@
package config
type Oracle struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (m *Oracle) Dsn() string {
return "oracle://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "/" + m.Dbname + "?" + m.Config
}
+17
View File
@@ -0,0 +1,17 @@
package config
type Pgsql struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
// Dsn 基于配置文件获取 dsn
// Author [SliverHorn](https://github.com/SliverHorn)
func (p *Pgsql) Dsn() string {
return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + p.Dbname + " port=" + p.Port + " " + p.Config
}
// LinkDsn 根据 dbname 生成 dsn
// Author [SliverHorn](https://github.com/SliverHorn)
func (p *Pgsql) LinkDsn(dbname string) string {
return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + dbname + " port=" + p.Port + " " + p.Config
}
+13
View File
@@ -0,0 +1,13 @@
package config
import (
"path/filepath"
)
type Sqlite struct {
GeneralDB `yaml:",inline" mapstructure:",squash"`
}
func (s *Sqlite) Dsn() string {
return filepath.Join(s.Path, s.Dbname+".db")
}
+8
View File
@@ -0,0 +1,8 @@
package config
type JWT struct {
SigningKey string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"` // jwt签名
ExpiresTime string `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
BufferTime string `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间
Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者
}
+41
View File
@@ -0,0 +1,41 @@
package config
import (
"fmt"
"strings"
)
type Mongo struct {
Coll string `json:"coll" yaml:"coll" mapstructure:"coll"` // collection name
Options string `json:"options" yaml:"options" mapstructure:"options"` // mongodb options
Database string `json:"database" yaml:"database" mapstructure:"database"` // database name
Username string `json:"username" yaml:"username" mapstructure:"username"` // 用户名
Password string `json:"password" yaml:"password" mapstructure:"password"` // 密码
AuthSource string `json:"auth-source" yaml:"auth-source" mapstructure:"auth-source"` // 验证数据库
MinPoolSize uint64 `json:"min-pool-size" yaml:"min-pool-size" mapstructure:"min-pool-size"` // 最小连接池
MaxPoolSize uint64 `json:"max-pool-size" yaml:"max-pool-size" mapstructure:"max-pool-size"` // 最大连接池
SocketTimeoutMs int64 `json:"socket-timeout-ms" yaml:"socket-timeout-ms" mapstructure:"socket-timeout-ms"` // socket超时时间
ConnectTimeoutMs int64 `json:"connect-timeout-ms" yaml:"connect-timeout-ms" mapstructure:"connect-timeout-ms"` // 连接超时时间
IsZap bool `json:"is-zap" yaml:"is-zap" mapstructure:"is-zap"` // 是否开启zap日志
Hosts []*MongoHost `json:"hosts" yaml:"hosts" mapstructure:"hosts"` // 主机列表
}
type MongoHost struct {
Host string `json:"host" yaml:"host" mapstructure:"host"` // ip地址
Port string `json:"port" yaml:"port" mapstructure:"port"` // 端口
}
// Uri .
func (x *Mongo) Uri() string {
length := len(x.Hosts)
hosts := make([]string, 0, length)
for i := 0; i < length; i++ {
if x.Hosts[i].Host != "" && x.Hosts[i].Port != "" {
hosts = append(hosts, x.Hosts[i].Host+":"+x.Hosts[i].Port)
}
}
if x.Options != "" {
return fmt.Sprintf("mongodb://%s/%s?%s", strings.Join(hosts, ","), x.Database, x.Options)
}
return fmt.Sprintf("mongodb://%s/%s", strings.Join(hosts, ","), x.Database)
}
+9
View File
@@ -0,0 +1,9 @@
package config
type OaLogin struct {
Url string `mapstructure:"url" json:"url" yaml:"url"`
Oauth2ClientId string `mapstructure:"oauth2-client-id" json:"oauth2-client-id" yaml:"oauth2-client-id"`
Oauth2ClientSecret string `mapstructure:"oauth2-client-secret" json:"oauth2-client-secret" yaml:"oauth2-client-secret"`
GetUserApiPath string `mapstructure:"get-user-info-api-path" json:"get-user-info-api-path" yaml:"get-user-info-api-path"`
GetTokenByCodeApiPath string `mapstructure:"get-token-by-code-api-path" json:"get-token-by-code-api-path" yaml:"get-token-by-code-api-path"`
}
+10
View File
@@ -0,0 +1,10 @@
package config
type AliyunOSS struct {
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
}
+13
View File
@@ -0,0 +1,13 @@
package config
type AwsS3 struct {
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
Region string `mapstructure:"region" json:"region" yaml:"region"`
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
}
+10
View File
@@ -0,0 +1,10 @@
package config
type CloudflareR2 struct {
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
Path string `mapstructure:"path" json:"path" yaml:"path"`
AccountID string `mapstructure:"account-id" json:"account-id" yaml:"account-id"`
AccessKeyID string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
SecretAccessKey string `mapstructure:"secret-access-key" json:"secret-access-key" yaml:"secret-access-key"`
}
+9
View File
@@ -0,0 +1,9 @@
package config
type HuaWeiObs struct {
Path string `mapstructure:"path" json:"path" yaml:"path"`
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"`
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
}
+6
View File
@@ -0,0 +1,6 @@
package config
type Local struct {
Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件访问路径
StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径
}
+11
View File
@@ -0,0 +1,11 @@
package config
type Minio struct {
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
UseSSL bool `mapstructure:"use-ssl" json:"use-ssl" yaml:"use-ssl"`
BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
}
+11
View File
@@ -0,0 +1,11 @@
package config
type Qiniu struct {
Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称
ImgPath string `mapstructure:"img-path" json:"img-path" yaml:"img-path"` // CDN加速域名
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` // 秘钥AK
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` // 秘钥SK
UseHTTPS bool `mapstructure:"use-https" json:"use-https" yaml:"use-https"` // 是否使用https
UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"use-cdn-domains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
}
+10
View File
@@ -0,0 +1,10 @@
package config
type TencentCOS struct {
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
Region string `mapstructure:"region" json:"region" yaml:"region"`
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
}
+10
View File
@@ -0,0 +1,10 @@
package config
type Redis struct {
Name string `mapstructure:"name" json:"name" yaml:"name"` // 代表当前实例的名字
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
DB int `mapstructure:"db" json:"db" yaml:"db"` // 单实例模式下redis的哪个数据库
UseCluster bool `mapstructure:"useCluster" json:"useCluster" yaml:"useCluster"` // 是否使用集群模式
ClusterAddrs []string `mapstructure:"clusterAddrs" json:"clusterAddrs" yaml:"clusterAddrs"` // 集群模式下的节点地址列表
}
+18
View File
@@ -0,0 +1,18 @@
package config
type System struct {
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // Oss类型
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值
LimitCountIP int `mapstructure:"iplimit-count" json:"iplimit-count" yaml:"iplimit-count"`
LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimit-time" yaml:"iplimit-time"`
UseMultipoint bool `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截
UseRedis bool `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"` // 使用redis
UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo
UseStrictAuth bool `mapstructure:"use-strict-auth" json:"use-strict-auth" yaml:"use-strict-auth"` // 使用树形角色分配模式
// Extend: Start Custom Configuration
UserDefaultGroupID string `mapstructure:"user_default-group-id" default:"888" json:"user_default-group-id" yaml:"user_default-group-id"` // 用户默认群组id
DockerRun bool `mapstructure:"docker-run" default:false json:"docker-run" yaml:"docker-run"` // 是否在docker中运行,如果是的话,无需自动生成jwtkey,直接填sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U与dify保持一致
// Extend: Stop Custom Configuration
}
+71
View File
@@ -0,0 +1,71 @@
package config
import (
"go.uber.org/zap/zapcore"
"time"
)
type Zap struct {
Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀
Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出
Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹
EncodeLevel string `mapstructure:"encode-level" json:"encode-level" yaml:"encode-level"` // 编码级
StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktrace-key" yaml:"stacktrace-key"` // 栈名
ShowLine bool `mapstructure:"show-line" json:"show-line" yaml:"show-line"` // 显示行
LogInConsole bool `mapstructure:"log-in-console" json:"log-in-console" yaml:"log-in-console"` // 输出控制台
RetentionDay int `mapstructure:"retention-day" json:"retention-day" yaml:"retention-day"` // 日志保留天数
}
// Levels 根据字符串转化为 zapcore.Levels
func (c *Zap) Levels() []zapcore.Level {
levels := make([]zapcore.Level, 0, 7)
level, err := zapcore.ParseLevel(c.Level)
if err != nil {
level = zapcore.DebugLevel
}
for ; level <= zapcore.FatalLevel; level++ {
levels = append(levels, level)
}
return levels
}
func (c *Zap) Encoder() zapcore.Encoder {
config := zapcore.EncoderConfig{
TimeKey: "time",
NameKey: "name",
LevelKey: "level",
CallerKey: "caller",
MessageKey: "message",
StacktraceKey: c.StacktraceKey,
LineEnding: zapcore.DefaultLineEnding,
EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(c.Prefix + t.Format("2006-01-02 15:04:05.000"))
},
EncodeLevel: c.LevelEncoder(),
EncodeCaller: zapcore.FullCallerEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
}
if c.Format == "json" {
return zapcore.NewJSONEncoder(config)
}
return zapcore.NewConsoleEncoder(config)
}
// LevelEncoder 根据 EncodeLevel 返回 zapcore.LevelEncoder
// Author [SliverHorn](https://github.com/SliverHorn)
func (c *Zap) LevelEncoder() zapcore.LevelEncoder {
switch {
case c.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
return zapcore.LowercaseLevelEncoder
case c.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
return zapcore.LowercaseColorLevelEncoder
case c.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
return zapcore.CapitalLevelEncoder
case c.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
return zapcore.CapitalColorLevelEncoder
default:
return zapcore.LowercaseLevelEncoder
}
}
+9
View File
@@ -0,0 +1,9 @@
package internal
const (
ConfigEnv = "GVA_CONFIG"
ConfigDefaultFile = "config.yaml"
ConfigTestFile = "config.test.yaml"
ConfigDebugFile = "config.debug.yaml"
ConfigReleaseFile = "config.release.yaml"
)
+121
View File
@@ -0,0 +1,121 @@
package internal
import (
"os"
"path/filepath"
"sync"
"time"
)
// Cutter 实现 io.Writer 接口
// 用于日志切割, strings.Join([]string{director,layout, formats..., level+".log"}, os.PathSeparator)
type Cutter struct {
level string // 日志级别(debug, info, warn, error, dpanic, panic, fatal)
layout string // 时间格式 2006-01-02 15:04:05
formats []string // 自定义参数([]string{Director,"2006-01-02", "business"(此参数可不写), level+".log"}
director string // 日志文件夹
retentionDay int //日志保留天数
file *os.File // 文件句柄
mutex *sync.RWMutex // 读写锁
}
type CutterOption func(*Cutter)
// CutterWithLayout 时间格式
func CutterWithLayout(layout string) CutterOption {
return func(c *Cutter) {
c.layout = layout
}
}
// CutterWithFormats 格式化参数
func CutterWithFormats(format ...string) CutterOption {
return func(c *Cutter) {
if len(format) > 0 {
c.formats = format
}
}
}
func NewCutter(director string, level string, retentionDay int, options ...CutterOption) *Cutter {
rotate := &Cutter{
level: level,
director: director,
retentionDay: retentionDay,
mutex: new(sync.RWMutex),
}
for i := 0; i < len(options); i++ {
options[i](rotate)
}
return rotate
}
// Write satisfies the io.Writer interface. It writes to the
// appropriate file handle that is currently being used.
// If we have reached rotation time, the target file gets
// automatically rotated, and also purged if necessary.
func (c *Cutter) Write(bytes []byte) (n int, err error) {
c.mutex.Lock()
defer func() {
if c.file != nil {
_ = c.file.Close()
c.file = nil
}
c.mutex.Unlock()
}()
length := len(c.formats)
values := make([]string, 0, 3+length)
values = append(values, c.director)
if c.layout != "" {
values = append(values, time.Now().Format(c.layout))
}
for i := 0; i < length; i++ {
values = append(values, c.formats[i])
}
values = append(values, c.level+".log")
filename := filepath.Join(values...)
director := filepath.Dir(filename)
err = os.MkdirAll(director, os.ModePerm)
if err != nil {
return 0, err
}
err = removeNDaysFolders(c.director, c.retentionDay)
if err != nil {
return 0, err
}
c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return 0, err
}
return c.file.Write(bytes)
}
func (c *Cutter) Sync() error {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.file != nil {
return c.file.Sync()
}
return nil
}
// 增加日志目录文件清理 小于等于零的值默认忽略不再处理
func removeNDaysFolders(dir string, days int) error {
if days <= 0 {
return nil
}
cutoff := time.Now().AddDate(0, 0, -days)
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() && info.ModTime().Before(cutoff) && path != dir {
err = os.RemoveAll(path)
if err != nil {
return err
}
}
return nil
})
}
+68
View File
@@ -0,0 +1,68 @@
package internal
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"time"
)
type ZapCore struct {
level zapcore.Level
zapcore.Core
}
func NewZapCore(level zapcore.Level) *ZapCore {
entity := &ZapCore{level: level}
syncer := entity.WriteSyncer()
levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool {
return l == level
})
entity.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, levelEnabler)
return entity
}
func (z *ZapCore) WriteSyncer(formats ...string) zapcore.WriteSyncer {
cutter := NewCutter(
global.GVA_CONFIG.Zap.Director,
z.level.String(),
global.GVA_CONFIG.Zap.RetentionDay,
CutterWithLayout(time.DateOnly),
CutterWithFormats(formats...),
)
if global.GVA_CONFIG.Zap.LogInConsole {
multiSyncer := zapcore.NewMultiWriteSyncer(os.Stdout, cutter)
return zapcore.AddSync(multiSyncer)
}
return zapcore.AddSync(cutter)
}
func (z *ZapCore) Enabled(level zapcore.Level) bool {
return z.level == level
}
func (z *ZapCore) With(fields []zapcore.Field) zapcore.Core {
return z.Core.With(fields)
}
func (z *ZapCore) Check(entry zapcore.Entry, check *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if z.Enabled(entry.Level) {
return check.AddCore(entry, z)
}
return check
}
func (z *ZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
for i := 0; i < len(fields); i++ {
if fields[i].Key == "business" || fields[i].Key == "folder" || fields[i].Key == "directory" {
syncer := z.WriteSyncer(fields[i].String)
z.Core = zapcore.NewCore(global.GVA_CONFIG.Zap.Encoder(), syncer, z.level)
}
}
return z.Core.Write(entry, fields)
}
func (z *ZapCore) Sync() error {
return z.Core.Sync()
}
+44
View File
@@ -0,0 +1,44 @@
package core
import (
"fmt"
cron "github.com/flipped-aurora/gin-vue-admin/server/corn"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/initialize"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"go.uber.org/zap"
)
type server interface {
ListenAndServe() error
}
func RunWindowsServer() {
if global.GVA_CONFIG.System.UseMultipoint || global.GVA_CONFIG.System.UseRedis {
// 初始化redis服务
initialize.Redis()
initialize.RedisList()
initialize.DifyRedis() // Extend: global code
}
if global.GVA_CONFIG.System.UseMongo {
err := initialize.Mongo.Initialization()
if err != nil {
zap.L().Error(fmt.Sprintf("%+v", err))
}
}
// 从db加载jwt数据
if global.GVA_DB != nil {
system.LoadAll()
}
Router := initialize.Routers()
cron.Corn() // Extend: corn job
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
s := initServer(address, Router)
global.GVA_LOG.Info("server run success on ", zap.String("address", address))
//
global.GVA_LOG.Error(s.ListenAndServe().Error())
}
+19
View File
@@ -0,0 +1,19 @@
//go:build !windows
// +build !windows
package core
import (
"time"
"github.com/fvbock/endless"
"github.com/gin-gonic/gin"
)
func initServer(address string, router *gin.Engine) server {
s := endless.NewServer(address, router)
s.ReadHeaderTimeout = 10 * time.Minute
s.WriteTimeout = 10 * time.Minute
s.MaxHeaderBytes = 1 << 20
return s
}
+21
View File
@@ -0,0 +1,21 @@
//go:build windows
// +build windows
package core
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func initServer(address string, router *gin.Engine) server {
return &http.Server{
Addr: address,
Handler: router,
ReadTimeout: 10 * time.Minute,
WriteTimeout: 10 * time.Minute,
MaxHeaderBytes: 1 << 20,
}
}
+72
View File
@@ -0,0 +1,72 @@
package core
import (
"flag"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/gin-gonic/gin"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"github.com/flipped-aurora/gin-vue-admin/server/global"
)
// Viper //
// 优先级: 命令行 > 环境变量 > 默认值
// Author [SliverHorn](https://github.com/SliverHorn)
func Viper(path ...string) *viper.Viper {
var config string
if len(path) == 0 {
flag.StringVar(&config, "c", "", "choose config file.")
flag.Parse()
if config == "" { // 判断命令行参数是否为空
if configEnv := os.Getenv(internal.ConfigEnv); configEnv == "" { // 判断 internal.ConfigEnv 常量存储的环境变量是否为空
switch gin.Mode() {
case gin.DebugMode:
config = internal.ConfigDefaultFile
case gin.ReleaseMode:
config = internal.ConfigReleaseFile
case gin.TestMode:
config = internal.ConfigTestFile
}
fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.Mode(), config)
} else { // internal.ConfigEnv 常量存储的环境变量不为空 将值赋值于config
config = configEnv
fmt.Printf("您正在使用%s环境变量,config的路径为%s\n", internal.ConfigEnv, config)
}
} else { // 命令行参数不为空 将值赋值于config
fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%s\n", config)
}
} else { // 函数传递的可变参数的第一个值赋值于config
config = path[0]
fmt.Printf("您正在使用func Viper()传递的值,config的路径为%s\n", config)
}
v := viper.New()
v.SetConfigFile(config)
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
fmt.Println(err)
}
})
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
panic(err)
}
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
return v
}
+32
View File
@@ -0,0 +1,32 @@
package core
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
// Zap 获取 zap.Logger
// Author [SliverHorn](https://github.com/SliverHorn)
func Zap() (logger *zap.Logger) {
if ok, _ := utils.PathExists(global.GVA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
fmt.Printf("create %v directory\n", global.GVA_CONFIG.Zap.Director)
_ = os.Mkdir(global.GVA_CONFIG.Zap.Director, os.ModePerm)
}
levels := global.GVA_CONFIG.Zap.Levels()
length := len(levels)
cores := make([]zapcore.Core, 0, length)
for i := 0; i < length; i++ {
core := internal.NewZapCore(levels[i])
cores = append(cores, core)
}
logger = zap.New(zapcore.NewTee(cores...))
if global.GVA_CONFIG.Zap.ShowLine {
logger = logger.WithOptions(zap.AddCaller())
}
return logger
}
+82
View File
@@ -0,0 +1,82 @@
package cron
import (
"context"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
gaiaReq "github.com/flipped-aurora/gin-vue-admin/server/model/gaia/request"
"github.com/flipped-aurora/gin-vue-admin/server/service/gaia"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/robfig/cron/v3"
"time"
)
// 返回一个支持至 秒 级别的 cron
func newWithSeconds() *cron.Cron {
secondParser := cron.NewParser(cron.Second | cron.Minute |
cron.Hour | cron.Dom | cron.Month | cron.DowOptional | cron.Descriptor)
return cron.New(cron.WithParser(secondParser), cron.WithChain())
}
func Corn() {
var lock bool
c := newWithSeconds()
// 每分钟同步一次用户列表
if _, err := c.AddFunc("0 */1 * * * *", func() {
if global.GVA_DB == nil {
global.GVA_LOG.Info("【定时任务-每1分钟执行1次】同步用户列表任务,数据库没有初始化,暂未开始同步")
return
}
if lock {
return
}
lock = true
user := system.UserExtendService{}
user.SyncUser()
gaia.SyncUserStatus()
lock = false
}); err != nil {
global.GVA_LOG.Fatal("Start Cron Error:" + err.Error())
time.Sleep(5)
return
}
global.GVA_LOG.Info("【定时任务-每1分钟执行1次】同步用户列表任务,已启动!")
// 每10分钟同步一次【应用使用分析数据】
if _, err := c.AddFunc("0 */10 * * * *", func() {
if global.GVA_DB == nil {
global.GVA_LOG.Info("【定时任务-每6分钟执行1次】同步应用使用分析数据任务,数据库没有初始化,暂未开始同步")
return
}
dashService := gaia.DashboardService{}
// 缓存前3页
for i := 1; i <= 3; i++ {
req := gaiaReq.GetAppQuotaRankingDataReq{
PageInfo: request.PageInfo{
Page: i,
PageSize: 10,
},
}
// 先删除缓存
cacheKey := fmt.Sprintf("app_token_quota_ranking:%d:%d", i, 10)
global.GVA_REDIS.Del(context.Background(), cacheKey)
// 再获取数据
_, _, err := dashService.GetAppQuotaRankingData(req)
if err != nil {
global.GVA_LOG.Error("每10分钟同步一次应用使用分析 获取信息出错:" + err.Error())
return
}
time.Sleep(time.Second * 10)
}
}); err != nil {
global.GVA_LOG.Fatal("每10分钟同步一次应用使用分析 出错:" + err.Error())
return
}
global.GVA_LOG.Info("【定时任务-每6分钟执行1次】同步应用使用分析数据任务,已启动!")
c.Start()
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
package global
import (
"fmt"
"sync"
"github.com/gin-gonic/gin"
"github.com/qiniu/qmgo"
"github.com/flipped-aurora/gin-vue-admin/server/utils/timer"
"github.com/songzhibin97/gkit/cache/local_cache"
"golang.org/x/sync/singleflight"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/config"
"github.com/redis/go-redis/v9"
"github.com/spf13/viper"
"gorm.io/gorm"
)
var (
GVA_DB *gorm.DB
GVA_DBList map[string]*gorm.DB
GVA_REDIS redis.UniversalClient
GVA_REDISList map[string]redis.UniversalClient
GVA_Dify_REDIS redis.UniversalClient // Extend: global code
GVA_MONGO *qmgo.QmgoClient
GVA_CONFIG config.Server
GVA_VP *viper.Viper
// GVA_LOG *oplogging.Logger
GVA_LOG *zap.Logger
GVA_Timer timer.Timer = timer.NewTimerTask()
GVA_Concurrency_Control = &singleflight.Group{}
GVA_ROUTERS gin.RoutesInfo
GVA_ACTIVE_DBNAME *string
BlackCache local_cache.Cache
lock sync.RWMutex
)
// GetGlobalDBByDBName 通过名称获取db list中的db
func GetGlobalDBByDBName(dbname string) *gorm.DB {
lock.RLock()
defer lock.RUnlock()
return GVA_DBList[dbname]
}
// MustGetGlobalDBByDBName 通过名称获取db 如果不存在则panic
func MustGetGlobalDBByDBName(dbname string) *gorm.DB {
lock.RLock()
defer lock.RUnlock()
db, ok := GVA_DBList[dbname]
if !ok || db == nil {
panic("db no init")
}
return db
}
func GetRedis(name string) redis.UniversalClient {
redis, ok := GVA_REDISList[name]
if !ok || redis == nil {
panic(fmt.Sprintf("redis `%s` no init", name))
}
return redis
}
+14
View File
@@ -0,0 +1,14 @@
package global
import (
"time"
"gorm.io/gorm"
)
type GVA_MODEL struct {
ID uint `gorm:"primarykey" json:"ID"` // 主键ID
CreatedAt time.Time // 创建时间
UpdatedAt time.Time // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
}

Some files were not shown because too many files have changed in this diff Show More