迁移项目到 RuoYi-Vue springboot2 基线
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -38,6 +38,7 @@ nbdist/
|
||||
|
||||
######################################################################
|
||||
# Others
|
||||
.DS_Store
|
||||
*.log
|
||||
*.xml.versionsBackup
|
||||
*.swp
|
||||
@@ -45,9 +46,3 @@ nbdist/
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
||||
|
||||
|
||||
logs/
|
||||
ruoyi-ui/dist.zip
|
||||
|
||||
.playwright-cli
|
||||
18
.playwright-cli/page-2026-04-11T07-16-36-054Z.yml
Normal file
18
.playwright-cli/page-2026-04-11T07-16-36-054Z.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- heading "上虞利率定价系统" [level=3] [ref=e5]
|
||||
- generic [ref=e8]:
|
||||
- textbox "账号" [ref=e9]
|
||||
- img [ref=e11]
|
||||
- generic [ref=e15]:
|
||||
- textbox "密码" [ref=e16]
|
||||
- img [ref=e18]
|
||||
- generic [ref=e20] [cursor=pointer]:
|
||||
- generic [ref=e21]:
|
||||
- checkbox "记住密码"
|
||||
- generic [ref=e23]: 记住密码
|
||||
- button "登 录" [ref=e26] [cursor=pointer]:
|
||||
- generic [ref=e27]: 登 录
|
||||
- generic [ref=e28]: Copyright © 2018-2026 RuoYi. All Rights Reserved.
|
||||
- text:
|
||||
20
.playwright-cli/page-2026-04-11T07-17-14-786Z.yml
Normal file
20
.playwright-cli/page-2026-04-11T07-17-14-786Z.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- heading "上虞利率定价系统" [level=3] [ref=e5]
|
||||
- generic [ref=e8]:
|
||||
- textbox "账号" [ref=e9]: admin123admin
|
||||
- img [ref=e11]
|
||||
- generic [ref=e14]:
|
||||
- generic [ref=e15]:
|
||||
- textbox "密码" [ref=e16]
|
||||
- img [ref=e18]
|
||||
- generic [ref=e29]: 请输入您的密码
|
||||
- generic [ref=e20] [cursor=pointer]:
|
||||
- generic [ref=e21]:
|
||||
- checkbox "记住密码"
|
||||
- generic [ref=e23]: 记住密码
|
||||
- button "登 录" [active] [ref=e26] [cursor=pointer]:
|
||||
- generic [ref=e27]: 登 录
|
||||
- generic [ref=e28]: Copyright © 2018-2026 RuoYi. All Rights Reserved.
|
||||
- text:
|
||||
1
.playwright-cli/page-2026-04-11T07-18-09-957Z.yml
Normal file
1
.playwright-cli/page-2026-04-11T07-18-09-957Z.yml
Normal file
@@ -0,0 +1 @@
|
||||
- generic [ref=e2]:
|
||||
191
.playwright-cli/page-2026-04-11T07-18-43-284Z.yml
Normal file
191
.playwright-cli/page-2026-04-11T07-18-43-284Z.yml
Normal file
@@ -0,0 +1,191 @@
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- link "上虞利率定价系统" [ref=e6] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e7]
|
||||
- heading "上虞利率定价系统" [level=1] [ref=e8]
|
||||
- menubar [ref=e12]:
|
||||
- link "流程列表" [ref=e14] [cursor=pointer]:
|
||||
- /url: /index
|
||||
- menuitem "流程列表" [ref=e15]:
|
||||
- img [ref=e16]
|
||||
- text: 流程列表
|
||||
- menuitem "系统管理 " [ref=e19]:
|
||||
- generic [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- text: 系统管理
|
||||
- generic [ref=e23]:
|
||||
- text:
|
||||
- generic [ref=e25]:
|
||||
- generic [ref=e26]:
|
||||
- generic [ref=e27]:
|
||||
- img [ref=e29] [cursor=pointer]
|
||||
- navigation "Breadcrumb" [ref=e31]:
|
||||
- generic:
|
||||
- generic [ref=e32]:
|
||||
- link "首页" [ref=e33]
|
||||
- text: /
|
||||
- generic [ref=e379]:
|
||||
- link "利率定价管理" [ref=e380]
|
||||
- text: /
|
||||
- link "流程列表" [ref=e382]
|
||||
- generic [ref=e36]:
|
||||
- img [ref=e38] [cursor=pointer]
|
||||
- img [ref=e41] [cursor=pointer]
|
||||
- button [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- button "若依" [ref=e48] [cursor=pointer]:
|
||||
- img [ref=e49]
|
||||
- text: 若依
|
||||
- generic [ref=e50]:
|
||||
- generic [ref=e53]:
|
||||
- generic [ref=e54] [cursor=pointer]: 流程列表
|
||||
- generic [ref=e383] [cursor=pointer]:
|
||||
- text: 流程详情
|
||||
- generic [ref=e384]:
|
||||
- text:
|
||||
- generic [ref=e385]:
|
||||
- generic [ref=e386]:
|
||||
- heading "流程详情" [level=2] [ref=e387]
|
||||
- button " 返回" [ref=e388] [cursor=pointer]:
|
||||
- generic [ref=e389]:
|
||||
- text: 返回
|
||||
- generic [ref=e391]:
|
||||
- generic [ref=e393]:
|
||||
- generic [ref=e396]: 关键信息
|
||||
- table [ref=e400]:
|
||||
- rowgroup [ref=e401]:
|
||||
- row "业务方流水号" [ref=e402]:
|
||||
- columnheader "业务方流水号" [ref=e403]
|
||||
- row "20260410150311114" [ref=e404]:
|
||||
- cell "20260410150311114" [ref=e405]
|
||||
- rowgroup [ref=e406]:
|
||||
- row "客户名称" [ref=e407]:
|
||||
- columnheader "客户名称" [ref=e408]
|
||||
- row "t***" [ref=e409]:
|
||||
- cell "t***" [ref=e410]
|
||||
- rowgroup [ref=e411]:
|
||||
- row "客户类型" [ref=e412]:
|
||||
- columnheader "客户类型" [ref=e413]
|
||||
- row "个人" [ref=e414]:
|
||||
- cell "个人" [ref=e415]
|
||||
- rowgroup [ref=e416]:
|
||||
- row "申请金额" [ref=e417]:
|
||||
- columnheader "申请金额" [ref=e418]
|
||||
- row "1000 元" [ref=e419]:
|
||||
- cell "1000 元" [ref=e420]
|
||||
- rowgroup [ref=e421]:
|
||||
- row "基准利率" [ref=e422]:
|
||||
- columnheader "基准利率" [ref=e423]
|
||||
- row "4.35 %" [ref=e424]:
|
||||
- cell "4.35 %" [ref=e425]
|
||||
- rowgroup [ref=e426]:
|
||||
- row "浮动BP" [ref=e427]:
|
||||
- columnheader "浮动BP" [ref=e428]
|
||||
- row "350" [ref=e429]:
|
||||
- cell "350" [ref=e430]
|
||||
- rowgroup [ref=e431]:
|
||||
- row "最终测算利率" [ref=e432]:
|
||||
- columnheader "最终测算利率" [ref=e433]
|
||||
- row "6.05 %" [ref=e434]:
|
||||
- cell "6.05 %" [ref=e435]
|
||||
- rowgroup [ref=e436]:
|
||||
- row "执行利率" [ref=e437]:
|
||||
- columnheader "执行利率" [ref=e438]
|
||||
- row "% 确定" [ref=e439]:
|
||||
- cell "% 确定" [ref=e440]:
|
||||
- generic [ref=e441]:
|
||||
- generic [ref=e442]:
|
||||
- textbox "请输入执行利率" [ref=e443]
|
||||
- generic [ref=e444]: "%"
|
||||
- button "确定" [ref=e445] [cursor=pointer]
|
||||
- generic [ref=e446]:
|
||||
- generic [ref=e447]:
|
||||
- generic [ref=e450]: 流程详情
|
||||
- generic [ref=e451]:
|
||||
- generic [ref=e452]:
|
||||
- heading "基本信息" [level=4] [ref=e453]
|
||||
- table [ref=e456]:
|
||||
- rowgroup [ref=e457]:
|
||||
- row "机构编码 892000 运行模式 1" [ref=e458]:
|
||||
- rowheader "机构编码" [ref=e459]
|
||||
- cell "892000" [ref=e460]
|
||||
- rowheader "运行模式" [ref=e461]
|
||||
- cell "1" [ref=e462]
|
||||
- rowgroup [ref=e463]:
|
||||
- row "客户内码 test 证件类型 身份证" [ref=e464]:
|
||||
- rowheader "客户内码" [ref=e465]
|
||||
- cell "test" [ref=e466]
|
||||
- rowheader "证件类型" [ref=e467]
|
||||
- cell "身份证" [ref=e468]
|
||||
- rowgroup [ref=e469]:
|
||||
- row "证件号码 ** 创建时间 2026-04-10 15:03:11" [ref=e470]:
|
||||
- rowheader "证件号码" [ref=e471]
|
||||
- cell "**" [ref=e472]
|
||||
- rowheader "创建时间" [ref=e473]
|
||||
- cell "2026-04-10 15:03:11" [ref=e474]
|
||||
- rowgroup [ref=e475]:
|
||||
- row "创建者 若依-admin" [ref=e476]:
|
||||
- rowheader "创建者" [ref=e477]
|
||||
- cell "若依-admin" [ref=e478]
|
||||
- generic [ref=e479]:
|
||||
- heading "业务信息" [level=4] [ref=e480]
|
||||
- table [ref=e483]:
|
||||
- rowgroup [ref=e484]:
|
||||
- row "担保方式 信用 申请金额 1000 元" [ref=e485]:
|
||||
- rowheader "担保方式" [ref=e486]
|
||||
- cell "信用" [ref=e487]
|
||||
- rowheader "申请金额" [ref=e488]
|
||||
- cell "1000 元" [ref=e489]
|
||||
- rowgroup [ref=e490]:
|
||||
- row "贷款用途 消费 借款期限 1" [ref=e491]:
|
||||
- rowheader "贷款用途" [ref=e492]
|
||||
- cell "消费" [ref=e493]
|
||||
- rowheader "借款期限" [ref=e494]
|
||||
- cell "1" [ref=e495]
|
||||
- rowgroup [ref=e496]:
|
||||
- row "是否有经营佐证 否 循环功能 否" [ref=e497]:
|
||||
- rowheader "是否有经营佐证" [ref=e498]
|
||||
- cell "否" [ref=e499]
|
||||
- rowheader "循环功能" [ref=e500]
|
||||
- cell "否" [ref=e501]
|
||||
- rowgroup [ref=e502]:
|
||||
- row "抵质押类型 - 抵质押物是否三方所有 否" [ref=e503]:
|
||||
- rowheader "抵质押类型" [ref=e504]
|
||||
- cell "-" [ref=e505]
|
||||
- rowheader "抵质押物是否三方所有" [ref=e506]
|
||||
- cell "否" [ref=e507]
|
||||
- generic [ref=e508]:
|
||||
- generic [ref=e511]: 模型输出
|
||||
- generic [ref=e513]:
|
||||
- generic [ref=e515]:
|
||||
- generic [ref=e517] [cursor=pointer]:
|
||||
- generic [ref=e519] [cursor=pointer]:
|
||||
- tablist [ref=e521]:
|
||||
- tab "基本信息" [selected] [ref=e523]
|
||||
- tab "忠诚度分析" [ref=e524]
|
||||
- tab "贡献度分析" [ref=e525]
|
||||
- tab "关联度分析" [ref=e526]
|
||||
- tab "贷款特征" [ref=e527]
|
||||
- tab "风险度分析" [ref=e528]
|
||||
- tab "测算结果" [ref=e529]
|
||||
- tabpanel "基本信息" [ref=e531]:
|
||||
- table [ref=e534]:
|
||||
- rowgroup [ref=e535]:
|
||||
- row "客户内码 CUST20260121001 客户名称 张*" [ref=e536]:
|
||||
- rowheader "客户内码" [ref=e537]
|
||||
- cell "CUST20260121001" [ref=e538]
|
||||
- rowheader "客户名称" [ref=e539]
|
||||
- cell "张*" [ref=e540]
|
||||
- rowgroup [ref=e541]:
|
||||
- row "证件类型 身份证 证件号码 3301********1234" [ref=e542]:
|
||||
- rowheader "证件类型" [ref=e543]
|
||||
- cell "身份证" [ref=e544]
|
||||
- rowheader "证件号码" [ref=e545]
|
||||
- cell "3301********1234" [ref=e546]
|
||||
- rowgroup [ref=e547]:
|
||||
- row "基准利率 4.35 %" [ref=e548]:
|
||||
- rowheader "基准利率" [ref=e549]
|
||||
- cell "4.35 %" [ref=e550]
|
||||
- text:
|
||||
18
.playwright-cli/page-2026-04-11T07-25-40-868Z.yml
Normal file
18
.playwright-cli/page-2026-04-11T07-25-40-868Z.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- heading "上虞利率定价系统" [level=3] [ref=e5]
|
||||
- generic [ref=e8]:
|
||||
- textbox "账号" [ref=e9]
|
||||
- img [ref=e11]
|
||||
- generic [ref=e15]:
|
||||
- textbox "密码" [ref=e16]
|
||||
- img [ref=e18]
|
||||
- generic [ref=e20] [cursor=pointer]:
|
||||
- generic [ref=e21]:
|
||||
- checkbox "记住密码"
|
||||
- generic [ref=e23]: 记住密码
|
||||
- button "登 录" [ref=e26] [cursor=pointer]:
|
||||
- generic [ref=e27]: 登 录
|
||||
- generic [ref=e28]: Copyright © 2018-2026 RuoYi. All Rights Reserved.
|
||||
- text:
|
||||
1
.playwright-cli/page-2026-04-11T07-25-55-827Z.yml
Normal file
1
.playwright-cli/page-2026-04-11T07-25-55-827Z.yml
Normal file
@@ -0,0 +1 @@
|
||||
- generic [ref=e2]:
|
||||
1
.playwright-cli/page-2026-04-11T07-26-20-300Z.yml
Normal file
1
.playwright-cli/page-2026-04-11T07-26-20-300Z.yml
Normal file
@@ -0,0 +1 @@
|
||||
- generic [ref=e2]:
|
||||
1
.playwright-cli/page-2026-04-11T07-26-34-973Z.yml
Normal file
1
.playwright-cli/page-2026-04-11T07-26-34-973Z.yml
Normal file
@@ -0,0 +1 @@
|
||||
- generic [ref=e2]:
|
||||
28
.playwright-cli/page-2026-04-14T05-22-21-308Z.yml
Normal file
28
.playwright-cli/page-2026-04-14T05-22-21-308Z.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- heading "若依管理系统" [level=3] [ref=e5]
|
||||
- generic [ref=e8]:
|
||||
- textbox "账号" [ref=e9]: admin
|
||||
- img [ref=e11]
|
||||
- generic [ref=e15]:
|
||||
- textbox "密码" [ref=e16]: admin123
|
||||
- img [ref=e18]
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- textbox "验证码" [ref=e23]
|
||||
- img [ref=e25]
|
||||
- generic [ref=e27]:
|
||||
- img
|
||||
- generic [ref=e28] [cursor=pointer]:
|
||||
- generic [ref=e29]:
|
||||
- checkbox "记住密码"
|
||||
- generic [ref=e31]: 记住密码
|
||||
- button "登 录" [ref=e34] [cursor=pointer]:
|
||||
- generic [ref=e35]: 登 录
|
||||
- generic [ref=e36]: Copyright © 2018-2026 RuoYi. All Rights Reserved.
|
||||
- text:
|
||||
- alert [ref=e37]:
|
||||
- generic [ref=e38]:
|
||||
- paragraph [ref=e39]: 系统接口500异常
|
||||
18
.playwright-cli/page-2026-04-14T05-39-30-657Z.yml
Normal file
18
.playwright-cli/page-2026-04-14T05-39-30-657Z.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
- generic [ref=e2]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- heading "若依管理系统" [level=3] [ref=e5]
|
||||
- generic [ref=e8]:
|
||||
- textbox "账号" [ref=e9]: admin
|
||||
- img [ref=e11]
|
||||
- generic [ref=e15]:
|
||||
- textbox "密码" [ref=e16]: admin123
|
||||
- img [ref=e18]
|
||||
- generic [ref=e20] [cursor=pointer]:
|
||||
- generic [ref=e21]:
|
||||
- checkbox "记住密码"
|
||||
- generic [ref=e23]: 记住密码
|
||||
- button "登 录" [ref=e26] [cursor=pointer]:
|
||||
- generic [ref=e27]: 登 录
|
||||
- generic [ref=e28]: Copyright © 2018-2026 RuoYi. All Rights Reserved.
|
||||
- text:
|
||||
33
README.md
33
README.md
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.2</h1>
|
||||
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.2-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
</p>
|
||||
|
||||
@@ -13,16 +13,37 @@
|
||||
|
||||
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||
|
||||
* 本仓库为RuoYi-Vue的Spring Boot 2 的版本,保持同步更新。
|
||||
* 前端采用Vue、Element UI。
|
||||
* 后端采用Spring Boot、Spring Security、Redis & Jwt。
|
||||
* 权限认证使用Jwt,支持多终端认证系统。
|
||||
* 支持加载动态权限菜单,多方式轻松权限控制。
|
||||
* 高效率开发,使用代码生成器可以一键生成前后端代码。
|
||||
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
|
||||
* 提供了单应用版本[RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast),Oracle版本[RuoYi-Vue-Oracle](https://gitcode.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
|
||||
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
|
||||
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)
|
||||
|
||||
# 版本分支
|
||||
|
||||
RuoYi-Vue 后端项目提供 Spring Boot 2.x / 3.x / 4.x 多版本分支的并行维护。
|
||||
|
||||
| 名称 | 说明 | 地址 |
|
||||
| :---------------- | :------------------------ | :------------------------------------------------------ |
|
||||
| master 默认分支 | Spring Boot 4.x (JDK 17+) | https://gitee.com/y_project/RuoYi-Vue |
|
||||
| springboot3 分支 | Spring Boot 3.x (JDK 17+) | https://gitee.com/y_project/RuoYi-Vue/tree/springboot3 |
|
||||
| springboot2 分支 | Spring Boot 2.x (JDK 8+) | https://gitee.com/y_project/RuoYi-Vue/tree/springboot2 |
|
||||
|
||||
RuoYi-Vue 前端项目提供 Vue 2.x / 3.x / JavaScript TypeScript 版本均可混用搭配
|
||||
|
||||
| 项目名称 | **RuoYi-Vue** | **RuoYi-Vue3** | **RuoYi-Vue3-TypeScript** |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **前端框架** | Vue 2 | Vue 3 | Vue 3 |
|
||||
| **脚本语言** | JavaScript | JavaScript | TypeScript |
|
||||
| **构建工具** | Vue CLI | Vite | Vite |
|
||||
| **UI 组件库** | Element UI | Element Plus | Element Plus |
|
||||
| **状态管理** | Vuex | Pinia | Pinia |
|
||||
| **路由管理** | Vue Router 3 | Vue Router 4 | Vue Router 4 |
|
||||
| **核心特点** | 1. 技术栈经典稳定<br>2. 社区资料丰富<br>3. 当前维护重心已转移 | 1. 现代前端技术栈<br>2. 开发体验与性能更优<br>3. 官方主推的活跃版本 | 1. 类型加持,减少沟通成本<br>2. 开发时有提示,效率更高<br>3. 多人协作企业级开发项目 |
|
||||
| **仓库地址** | [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) | [RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3) | [RuoYi-Vue3-TypeScript](https://gitcode.com/yangzongzhuan/RuoYi-Vue3/tree/typescript) |
|
||||
|
||||
## 内置功能
|
||||
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
@@ -92,4 +113,4 @@
|
||||
|
||||
## 若依前后端分离交流群
|
||||
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=M9y5NjAl44lAL_Vh2crmEehZU_PMU6KS&authKey=ZSDz8hEREWSaPuxQV3gEwqGIaGjfRNnkB4rJjf0IvXhrSUGSGwQFmBA%2Boe8HFxyl&noverify=0&group_code=127358632) 点击按钮入群。
|
||||
BIN
bin/.DS_Store
vendored
BIN
bin/.DS_Store
vendored
Binary file not shown.
0
bin/prod/restart_java_test.sh
Normal file → Executable file
0
bin/prod/restart_java_test.sh
Normal file → Executable file
0
bin/restart_java_backend_test.sh
Normal file → Executable file
0
bin/restart_java_backend_test.sh
Normal file → Executable file
43
doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md
Normal file
43
doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# 本地 Tomcat 与 TongWeb 打包并存后端实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
- 恢复本地 `jar + 内嵌 Tomcat` 启动链路
|
||||
- 保留服务器 `war + TongWeb` 部署链路
|
||||
- 一次 `mvn package` 同时产出 `ruoyi-admin.jar` 和 `ruoyi-admin.war`
|
||||
|
||||
## 改动范围
|
||||
|
||||
- Maven 打包配置
|
||||
- 后端启动与部署脚本
|
||||
- 脚本测试
|
||||
- 相关运行文档和实施记录
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. 先修改脚本测试,重新定义目标行为
|
||||
- 本地测试脚本期望 `restart_java_backend.sh` 使用 `java -jar`
|
||||
- 生产测试脚本继续期望 TongWeb 使用 `war`
|
||||
2. 调整 Maven 打包配置
|
||||
- `ruoyi-admin` 恢复主产物 `jar`
|
||||
- 增加附加 `war` 产物
|
||||
- 恢复本地运行所需的内嵌 Tomcat 依赖
|
||||
3. 调整脚本
|
||||
- 本地脚本改回管理 `ruoyi-admin.jar`
|
||||
- 生产脚本继续管理 `ruoyi-admin.war`
|
||||
4. 更新文档
|
||||
- 更新运行说明
|
||||
- 新增本次实施记录
|
||||
5. 执行验证
|
||||
|
||||
## 验证要求
|
||||
|
||||
- `sh bin/restart_java_backend_test.sh`
|
||||
- `sh bin/prod/restart_java_test.sh`
|
||||
- `sh bin/prod/deploy_from_package_test.sh`
|
||||
- `sh -n bin/restart_java_backend.sh`
|
||||
- `sh -n bin/prod/restart_java.sh`
|
||||
- `sh -n bin/prod/deploy_from_package.sh`
|
||||
- `mvn -pl ruoyi-admin -am package -DskipTests`
|
||||
- 确认 `ruoyi-admin/target/ruoyi-admin.jar`
|
||||
- 确认 `ruoyi-admin/target/ruoyi-admin.war`
|
||||
147
doc/2026-04-13-local-tomcat-remote-tongweb-design.md
Normal file
147
doc/2026-04-13-local-tomcat-remote-tongweb-design.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# 本地 Tomcat 运行与 TongWeb 打包并存设计
|
||||
|
||||
## 背景
|
||||
|
||||
当前项目已经被调整为统一的 TongWeb `war` 交付模式,这会导致本地开发时也必须围绕 TongWeb 组织启动流程,不符合当前开发诉求。
|
||||
|
||||
本次目标是同时保留两条链路:
|
||||
|
||||
- 本地开发运行继续使用内嵌 Tomcat
|
||||
- 打包交付继续支持服务器上的 TongWeb
|
||||
|
||||
并且要求一次 `mvn package` 同时产出本地运行所需的 `jar` 和服务器部署所需的 `war`。
|
||||
|
||||
## 目标
|
||||
|
||||
- 保留 `IDEA`、`mvn spring-boot:run`、本地脚本直启后端的开发体验
|
||||
- 保留面向 TongWeb 的 `war` 交付方式
|
||||
- `mvn package` 后同时得到 `ruoyi-admin.jar` 与 `ruoyi-admin.war`
|
||||
- 本地不强依赖安装 TongWeb
|
||||
- 服务器部署脚本继续只消费 `war`
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不新增第二个后端启动模块
|
||||
- 不拆分额外的部署工程
|
||||
- 不修改前端构建方式
|
||||
- 不引入“兼容模式”“降级模式”之类额外分支逻辑
|
||||
|
||||
## 设计方案
|
||||
|
||||
### 1. 构建产物设计
|
||||
|
||||
`ruoyi-admin` 恢复为以 `jar` 为主产物的 Spring Boot 应用,用于本地开发运行。
|
||||
|
||||
在同一个 Maven 模块中补充 `war` 打包步骤,使一次 `mvn package` 后同时得到:
|
||||
|
||||
- `ruoyi-admin/target/ruoyi-admin.jar`
|
||||
- `ruoyi-admin/target/ruoyi-admin.war`
|
||||
|
||||
这样本地和服务器都从同一套源码构建,但消费不同产物:
|
||||
|
||||
- 本地消费 `jar`
|
||||
- 服务器消费 `war`
|
||||
|
||||
### 2. 依赖设计
|
||||
|
||||
为了保证本地可继续走内嵌 Tomcat:
|
||||
|
||||
- 恢复 Web 模块中的内嵌 Tomcat 依赖链
|
||||
- 保持 `spring-boot:run` 与 `java -jar` 均可正常工作
|
||||
|
||||
为了保证 TongWeb 外部容器部署:
|
||||
|
||||
- 打出的 `war` 不能把容器自身实现错误打包成部署冲突形式
|
||||
- `Servlet API` 继续按外部容器提供的思路处理
|
||||
|
||||
本质上,本地运行和 TongWeb 部署共享同一套业务代码,但运行容器不同。
|
||||
|
||||
### 3. 启动脚本设计
|
||||
|
||||
#### 本地脚本
|
||||
|
||||
`bin/restart_java_backend.sh` 恢复为本地开发脚本:
|
||||
|
||||
- 执行 Maven 打包
|
||||
- 使用 `ruoyi-admin.jar`
|
||||
- 通过 `java -jar` 管理本地后端进程
|
||||
|
||||
这条链路不再依赖 `TONGWEB_HOME`。
|
||||
|
||||
#### 生产脚本
|
||||
|
||||
以下脚本保持 TongWeb 交付模型:
|
||||
|
||||
- `bin/prod/restart_java.sh`
|
||||
- `bin/prod/deploy_from_package.sh`
|
||||
- `bin/prod/deploy_release.sh`
|
||||
|
||||
它们继续只处理 `ruoyi-admin.war`,不回退到 `jar`。
|
||||
|
||||
### 4. 本地与服务器联调设计
|
||||
|
||||
本地开发时不要求本机安装 TongWeb。
|
||||
|
||||
如果需要验证 TongWeb 运行环境,只通过两种方式完成:
|
||||
|
||||
- 打包后部署到服务器 TongWeb 验证
|
||||
- 本地系统直接调用服务器上已部署的 TongWeb 地址联调
|
||||
|
||||
这意味着:
|
||||
|
||||
- 本地开发链路只围绕 `jar + Tomcat`
|
||||
- 服务器部署链路只围绕 `war + TongWeb`
|
||||
|
||||
## 验证方案
|
||||
|
||||
### 构建验证
|
||||
|
||||
执行:
|
||||
|
||||
```sh
|
||||
mvn -pl ruoyi-admin -am package -DskipTests
|
||||
```
|
||||
|
||||
确认同时存在:
|
||||
|
||||
- `ruoyi-admin/target/ruoyi-admin.jar`
|
||||
- `ruoyi-admin/target/ruoyi-admin.war`
|
||||
|
||||
### 本地运行验证
|
||||
|
||||
执行:
|
||||
|
||||
```sh
|
||||
sh bin/restart_java_backend.sh restart
|
||||
```
|
||||
|
||||
确认本地以 `java -jar` 正常运行。
|
||||
|
||||
### TongWeb 脚本验证
|
||||
|
||||
执行:
|
||||
|
||||
```sh
|
||||
sh bin/prod/restart_java_test.sh
|
||||
sh bin/prod/deploy_from_package_test.sh
|
||||
```
|
||||
|
||||
确认 TongWeb 侧仍围绕 `war` 工作。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- `ruoyi-admin` Maven 打包配置
|
||||
- Web 相关模块的容器依赖声明
|
||||
- 本地后端脚本
|
||||
- 生产 TongWeb 脚本
|
||||
- 运行文档与实施记录
|
||||
|
||||
## 结论
|
||||
|
||||
本方案采用最短路径实现“双产物、双运行链路并存”:
|
||||
|
||||
- 本地运行继续走内嵌 Tomcat
|
||||
- 服务器部署继续走 TongWeb
|
||||
- 一次打包同时产出 `jar` 与 `war`
|
||||
|
||||
在不新增模块、不扩散复杂度的前提下,满足开发与部署两端的实际需要。
|
||||
39
doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md
Normal file
39
doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# 东方通替换 Tomcat 后端实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
- 将后端交付形态从内嵌 Tomcat 的 `jar` 调整为部署到东方通 TongWeb 的 `war`
|
||||
- 清理当前发布链路中围绕 `java -jar` / `ruoyi-admin.jar` 的脚本约定
|
||||
- 保持现有前端发布方式和 Nginx 入口不变,后端仍沿用 `63310` 作为反向代理目标端口
|
||||
|
||||
## 改动范围
|
||||
|
||||
- Maven 构建
|
||||
- 调整 `ruoyi-admin` 打包类型为 `war`
|
||||
- 去除模块链路中的嵌入式 Tomcat 打包依赖
|
||||
- 明确 Servlet API 由外部容器提供
|
||||
- 部署脚本
|
||||
- 将生产部署脚本中的后端产物从 `ruoyi-admin.jar` 切换为 `ruoyi-admin.war`
|
||||
- 将生产重启脚本从 Java 进程启停改为 TongWeb 容器启停与 `war` 发布
|
||||
- 调整本地后端重启脚本,使其面向 TongWeb 进行构建和部署
|
||||
- 运行文档
|
||||
- 更新本地安装手册中的后端环境说明,改为 TongWeb
|
||||
- 新增本次改动实施记录
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. 先修改现有脚本测试,明确新的 `war + TongWeb` 约束
|
||||
2. 调整 Maven 配置,产出 `ruoyi-admin.war`
|
||||
3. 修改生产部署脚本和本地重启脚本
|
||||
4. 更新运行文档与实施记录
|
||||
5. 执行脚本测试、语法校验和 Maven 打包验证
|
||||
|
||||
## 验证要求
|
||||
|
||||
- `mvn -pl ruoyi-admin -am clean package -DskipTests` 成功,且产物为 `ruoyi-admin.war`
|
||||
- `sh bin/prod/restart_java_test.sh` 成功
|
||||
- `sh bin/prod/deploy_from_package_test.sh` 成功
|
||||
- `sh bin/restart_java_backend_test.sh` 成功
|
||||
- `sh -n bin/prod/restart_java.sh`
|
||||
- `sh -n bin/prod/deploy_from_package.sh`
|
||||
- `sh -n bin/restart_java_backend.sh`
|
||||
41
doc/2026-04-14-ruoyi-vue-springboot2-backend-plan.md
Normal file
41
doc/2026-04-14-ruoyi-vue-springboot2-backend-plan.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# RuoYi-Vue springboot2 后端迁移实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
以上游 `RuoYi-Vue/springboot2` 为后端框架基线,将当前项目的后端框架层整体回退并重对齐到 Spring Boot 2 / Java 8,同时恢复 `ruoyi-loan-pricing` 业务模块和管理端业务接入配置。
|
||||
|
||||
## 范围
|
||||
|
||||
- 根 `pom.xml`
|
||||
- `ruoyi-admin`
|
||||
- `ruoyi-common`
|
||||
- `ruoyi-framework`
|
||||
- `ruoyi-generator`
|
||||
- `ruoyi-quartz`
|
||||
- `ruoyi-system`
|
||||
- `ruoyi-loan-pricing`
|
||||
- `sql`
|
||||
|
||||
## 执行步骤
|
||||
|
||||
1. 备份当前后端业务模块与业务配置
|
||||
2. 用上游 `springboot2` 覆盖根 POM 和基础后端模块
|
||||
3. 恢复 `ruoyi-loan-pricing` 模块目录
|
||||
4. 在根 POM 与 `ruoyi-admin/pom.xml` 中重新挂载 `ruoyi-loan-pricing`
|
||||
5. 恢复 `ruoyi-admin/src/main/resources` 中的 `loan-pricing` 业务配置
|
||||
6. 检查并修正 `ruoyi-loan-pricing` 中不兼容 Spring Boot 2 的依赖、注解和包引用
|
||||
7. 校正 Mapper、资源文件和测试依赖,保证模块能参与 Maven 聚合构建
|
||||
8. 保留并整理业务 SQL 脚本
|
||||
9. 在 Java 8 环境下执行后端编译与关键测试
|
||||
|
||||
## 验证要求
|
||||
|
||||
- `mvn -pl ruoyi-admin -am test` 至少能够完成依赖解析和关键模块测试
|
||||
- `mvn -pl ruoyi-admin -am package -DskipTests` 能通过
|
||||
- `ruoyi-loan-pricing` 模块可被 `ruoyi-admin` 正常引用
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 不保留 Spring Boot 3 / Java 17 双配置
|
||||
- 不引入兼容层或过渡层
|
||||
- 若业务模块使用了 Boot 3 专属依赖,直接改为 Boot 2 可运行实现
|
||||
37
doc/2026-04-14-ruoyi-vue-springboot2-frontend-plan.md
Normal file
37
doc/2026-04-14-ruoyi-vue-springboot2-frontend-plan.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# RuoYi-Vue springboot2 前端迁移实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
以上游 `RuoYi-Vue/springboot2` 为前端基线,将当前项目前端整体回退并重对齐到上游 `ruoyi-ui`,然后恢复 `loanPricing` 业务页面、接口调用、路由与相关依赖。
|
||||
|
||||
## 范围
|
||||
|
||||
- `ruoyi-ui/package.json`
|
||||
- `ruoyi-ui/src`
|
||||
- `ruoyi-ui/public`
|
||||
- `ruoyi-ui/build`
|
||||
- `ruoyi-ui/tests`
|
||||
|
||||
## 执行步骤
|
||||
|
||||
1. 备份当前 `loanPricing` 页面、接口文件、路由改动和业务测试脚本
|
||||
2. 用上游 `springboot2` 的 `ruoyi-ui` 覆盖当前前端框架层
|
||||
3. 恢复 `src/views/loanPricing` 页面目录
|
||||
4. 恢复 `src/api/loanPricing` 接口文件
|
||||
5. 将业务路由重新挂回 `src/router/index.js`
|
||||
6. 恢复业务所需的前端依赖与测试脚本
|
||||
7. 用 `nvm` 切换到合适的 Node 版本后重新安装依赖
|
||||
8. 执行前端构建与页面联调验证
|
||||
|
||||
## 验证要求
|
||||
|
||||
- `npm install` 成功
|
||||
- `npm run build:prod` 成功
|
||||
- `loanPricing` 页面路由可访问
|
||||
- 页面基础交互和接口调用链路未丢失
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 前端直接以 `springboot2` 上游为准,不保留当前非业务性的历史前端定制
|
||||
- Node 版本必须通过 `nvm` 控制
|
||||
- 测试完成后要关闭前端调试进程
|
||||
120
doc/2026-04-14-ruoyi-vue-springboot2-migration-design.md
Normal file
120
doc/2026-04-14-ruoyi-vue-springboot2-migration-design.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# RuoYi-Vue springboot2 基线迁移设计
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本次迁移以上游 `https://gitee.com/y_project/RuoYi-Vue/tree/springboot2` 为唯一框架基线,先将当前仓库整体回退并重对齐到该基线,再迁回现有业务模块与业务页面,最终形成一个“框架层跟随上游、业务层保留现状”的项目结构。
|
||||
|
||||
本次迁移不采用兼容层、补丁层或双栈并存方案,不保留 Spring Boot 3 / Java 17 的框架实现。
|
||||
|
||||
## 2. 现状与目标差异
|
||||
|
||||
当前仓库已经是 RuoYi 多模块工程,但后端已升级到 `Spring Boot 3.5.x`、`Java 17`,并引入了以下业务定制:
|
||||
|
||||
- 后端业务模块:`ruoyi-loan-pricing`
|
||||
- 前端业务页面:`ruoyi-ui/src/views/loanPricing`
|
||||
- 前端业务接口:`ruoyi-ui/src/api/loanPricing`
|
||||
- 管理端业务接入:`ruoyi-admin` 中的业务依赖与配置
|
||||
- 业务 SQL:`sql/loan_pricing_*`、`sql/model_*`、`sql/loan_pricing_menu.sql`
|
||||
|
||||
目标上游 `springboot2` 分支采用:
|
||||
|
||||
- `RuoYi-Vue 3.9.2`
|
||||
- `Spring Boot 2.5.15`
|
||||
- `Java 8`
|
||||
- `Vue 2 + Element UI`
|
||||
|
||||
因此本次迁移的本质是:先将框架层彻底切回 Spring Boot 2 基线,再把利率定价业务重新挂载到新的基线上。
|
||||
|
||||
## 3. 迁移范围
|
||||
|
||||
### 3.1 框架层
|
||||
|
||||
以下内容以上游 `springboot2` 版本为准:
|
||||
|
||||
- 根目录框架文件与脚本
|
||||
- 根 `pom.xml`
|
||||
- `ruoyi-admin`
|
||||
- `ruoyi-common`
|
||||
- `ruoyi-framework`
|
||||
- `ruoyi-generator`
|
||||
- `ruoyi-quartz`
|
||||
- `ruoyi-system`
|
||||
- `ruoyi-ui`
|
||||
- 上游自带 `sql` 基础脚本
|
||||
|
||||
### 3.2 业务层
|
||||
|
||||
以下内容需要从当前仓库迁回到新基线:
|
||||
|
||||
- `ruoyi-loan-pricing` 全模块
|
||||
- `ruoyi-admin` 中与 `loan-pricing` 相关的业务配置、业务依赖
|
||||
- `ruoyi-ui/src/views/loanPricing` 页面
|
||||
- `ruoyi-ui/src/api/loanPricing` 接口文件
|
||||
- `ruoyi-ui/src/router/index.js` 中的业务路由
|
||||
- 业务 SQL、部署脚本、项目文档
|
||||
|
||||
## 4. 实施策略
|
||||
|
||||
### 4.1 基线覆盖策略
|
||||
|
||||
采用“上游覆盖 + 业务回贴”模式:
|
||||
|
||||
1. 先备份当前业务目录与业务配置
|
||||
2. 将上游 `springboot2` 内容覆盖到当前仓库
|
||||
3. 恢复业务模块、业务页面、业务配置和业务 SQL
|
||||
4. 处理 Spring Boot 2 下的编译和运行差异
|
||||
|
||||
### 4.2 后端回贴策略
|
||||
|
||||
后端只保留一套 Spring Boot 2 实现,不额外保留 Spring Boot 3 兼容写法。若 `ruoyi-loan-pricing` 内存在 Boot 3 / Jakarta / SpringDoc 相关依赖或 API,则直接改回 Boot 2 可运行写法。
|
||||
|
||||
### 4.3 前端回贴策略
|
||||
|
||||
前端以 `springboot2` 上游 `ruoyi-ui` 为基础,回贴 `loanPricing` 页面、接口调用、路由和必要依赖;不保留与当前业务无关的历史定制。
|
||||
|
||||
## 5. 风险点与处理原则
|
||||
|
||||
### 5.1 框架依赖回退风险
|
||||
|
||||
风险:
|
||||
|
||||
- `springdoc`、`jakarta.*`、Boot 3 专用依赖不兼容 Boot 2
|
||||
- 测试依赖、插件版本、JDK 版本需要一并回退
|
||||
|
||||
处理原则:
|
||||
|
||||
- 以 Boot 2 上游依赖为准
|
||||
- 业务模块仅保留完成业务所必需的依赖
|
||||
|
||||
### 5.2 业务接入点遗漏风险
|
||||
|
||||
风险:
|
||||
|
||||
- `ruoyi-admin` 配置遗漏
|
||||
- 前端路由、菜单、接口路径遗漏
|
||||
- SQL 脚本与权限菜单脚本遗漏
|
||||
|
||||
处理原则:
|
||||
|
||||
- 逐类枚举迁移对象
|
||||
- 迁移后通过编译、页面访问、接口调用进行闭环验证
|
||||
|
||||
## 6. 验证标准
|
||||
|
||||
迁移完成后至少满足以下条件:
|
||||
|
||||
- Maven 多模块在 Spring Boot 2 / Java 8 环境下可编译
|
||||
- `ruoyi-admin` 可启动
|
||||
- `ruoyi-ui` 在指定 Node 版本下可安装并构建
|
||||
- `loanPricing` 页面路由可访问
|
||||
- 利率定价相关接口类与 Mapper 可通过编译
|
||||
- 业务 SQL 与菜单脚本仍保留在仓库内
|
||||
|
||||
## 7. 产出物
|
||||
|
||||
本次任务最终需产出:
|
||||
|
||||
- 本设计文档
|
||||
- 后端实施计划文档
|
||||
- 前端实施计划文档
|
||||
- 迁移实施记录文档
|
||||
@@ -0,0 +1,34 @@
|
||||
# 本地 Tomcat 与 TongWeb 双产物实施记录
|
||||
|
||||
## 本次改动
|
||||
|
||||
- 将 `ruoyi-admin` 的主打包方式从 `war` 恢复为 `jar`
|
||||
- 恢复 `spring-boot-maven-plugin` 的 `repackage`,保证本地可直接运行 `ruoyi-admin.jar`
|
||||
- 在 `ruoyi-admin` 中增加附加 `war` 打包步骤,使 `mvn package` 同时产出:
|
||||
- `ruoyi-admin.jar`
|
||||
- `ruoyi-admin.war`
|
||||
- 在 `war` 打包中排除内嵌 Tomcat 相关 jar,避免 TongWeb 部署时容器冲突
|
||||
- 将 `bin/restart_java_backend.sh` 恢复为本地 `java -jar` 启动链路
|
||||
- 保持 `bin/prod/restart_java.sh`、`bin/prod/deploy_from_package.sh` 继续消费 `ruoyi-admin.war`
|
||||
- 更新 `bin/run.bat`,恢复为本地 `jar` 启动入口
|
||||
- 新增设计文档 `doc/2026-04-13-local-tomcat-remote-tongweb-design.md`
|
||||
- 新增实施计划 `doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md`
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 已执行 `sh bin/restart_java_backend_test.sh`
|
||||
- 已执行 `sh bin/prod/restart_java_test.sh`
|
||||
- 已执行 `sh bin/prod/deploy_from_package_test.sh`
|
||||
- 已执行 `sh -n bin/restart_java_backend.sh`
|
||||
- 已执行 `sh -n bin/prod/restart_java.sh`
|
||||
- 已执行 `sh -n bin/prod/deploy_from_package.sh`
|
||||
- 已执行 `mvn -pl ruoyi-admin -am clean package -DskipTests`
|
||||
- 已确认产物:
|
||||
- `ruoyi-admin/target/ruoyi-admin.jar`
|
||||
- `ruoyi-admin/target/ruoyi-admin.war`
|
||||
|
||||
## 结果说明
|
||||
|
||||
- 本地开发运行继续使用内嵌 Tomcat,不要求本机安装 TongWeb
|
||||
- 服务器部署继续使用 TongWeb,只消费 `war`
|
||||
- 一次打包即可同时得到本地运行产物和 TongWeb 部署产物
|
||||
@@ -0,0 +1,25 @@
|
||||
# Tomcat 替换为东方通实施记录
|
||||
|
||||
## 本次改动
|
||||
|
||||
- 将 `ruoyi-admin` 打包方式从 `jar` 调整为 `war`
|
||||
- 在 `ruoyi-admin` 中显式声明 `spring-boot-starter-tomcat` 为 `provided`
|
||||
- 在 `ruoyi-framework`、`ruoyi-loan-pricing` 中排除 `spring-boot-starter-web` 传递进来的嵌入式 Tomcat
|
||||
- 将 `ruoyi-common` 中的 `jakarta.servlet-api` 调整为 `provided`
|
||||
- 删除 `application-dev.yml`、`application-uat.yml`、`application-pro.yml` 中仅对内嵌 Tomcat 生效的 `server.tomcat` 配置
|
||||
- 将 `bin/prod/restart_java.sh` 从 `java -jar` 启停改为 TongWeb 启停与 `war` 同步
|
||||
- 将 `bin/prod/deploy_from_package.sh`、`bin/prod/deploy_release.sh` 的后端交付物从 `ruoyi-admin.jar` 改为 `ruoyi-admin.war`
|
||||
- 将 `bin/restart_java_backend.sh` 改为本地构建 `war` 并发布到 TongWeb
|
||||
- 更新 `deploy/2026-03-31-local-nginx-java-install-manual.md`,将后端运行环境说明改为 TongWeb
|
||||
- 新增后端实施计划 `doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md`
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 已执行 `sh bin/prod/restart_java_test.sh`
|
||||
- 已执行 `sh bin/prod/deploy_from_package_test.sh`
|
||||
- 已执行 `sh bin/restart_java_backend_test.sh`
|
||||
|
||||
## 说明
|
||||
|
||||
- 本次替换按当前项目 `Spring Boot 3 + Jakarta Servlet` 路线落地,要求实际使用的东方通版本能够承载 Jakarta 体系应用
|
||||
- Nginx 入口和反向代理端口保持不变,仍通过 `63310` 转发到后端容器
|
||||
@@ -0,0 +1,105 @@
|
||||
# RuoYi-Vue springboot2 基线迁移实施记录
|
||||
|
||||
## 本次完成内容
|
||||
|
||||
- 以 `RuoYi-Vue/springboot2` 为基线覆盖当前仓库的框架层:
|
||||
- 根 `pom.xml`
|
||||
- `ruoyi-admin`
|
||||
- `ruoyi-common`
|
||||
- `ruoyi-framework`
|
||||
- `ruoyi-generator`
|
||||
- `ruoyi-quartz`
|
||||
- `ruoyi-system`
|
||||
- `ruoyi-ui`
|
||||
- `sql` 基础脚本
|
||||
- 恢复并接回业务模块与业务页面:
|
||||
- `ruoyi-loan-pricing`
|
||||
- `ruoyi-ui/src/views/loanPricing`
|
||||
- `ruoyi-ui/src/api/loanPricing`
|
||||
- `ruoyi-ui/src/router/index.js` 中的业务路由
|
||||
- `ruoyi-admin` 中的业务配置
|
||||
- 将 `ruoyi-loan-pricing` 从 Boot 3 / Java 17 写法回退到 Boot 2 / Java 8 可编译形态:
|
||||
- 移除 `springdoc` 注解和依赖
|
||||
- 将 `jakarta.*` 改回 `javax.*`
|
||||
- 将 `String.repeat` 改为 Java 8 兼容实现
|
||||
- 将模型调用改为模块内 `RestTemplate + form-urlencoded`
|
||||
- 补回当前项目的“无 Redis”本地缓存实现:
|
||||
- `InMemoryCacheEntry`
|
||||
- `InMemoryCacheStats`
|
||||
- `InMemoryCacheStore`
|
||||
- 本地缓存版 `RedisCache`
|
||||
- 本地缓存版 `CacheController`
|
||||
- 调整前端业务依赖与脚本:
|
||||
- 恢复 `crypto-js`
|
||||
- 恢复 `splitpanes`
|
||||
- 恢复 `ruoyi-ui/tests` 下 4 个业务测试脚本
|
||||
- 恢复项目原有部署辅助脚本:
|
||||
- `bin/prod/*.sh`
|
||||
- `bin/restart_java_backend*.sh`
|
||||
- 修正本地启动所需配置:
|
||||
- `logback.xml` 日志目录改为项目内 `logs`
|
||||
- `application.yml` 补齐 `swagger`、`redis`、`mybatis`
|
||||
- 增加 `ruoyi-loan-pricing` 模块挂载和依赖声明
|
||||
|
||||
## 验证结果
|
||||
|
||||
### 后端编译验证
|
||||
|
||||
已通过:
|
||||
|
||||
- `export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home && export PATH="$JAVA_HOME/bin:$PATH" && mvn -pl ruoyi-admin -am -DskipTests package`
|
||||
- `export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home && export PATH="$JAVA_HOME/bin:$PATH" && mvn -pl ruoyi-admin -am install -DskipTests`
|
||||
|
||||
### 前端构建验证
|
||||
|
||||
已通过:
|
||||
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 && npm install`
|
||||
- `source ~/.nvm/nvm.sh && nvm use 14.21.3 && npm run build:prod`
|
||||
|
||||
### 前后端联调验证
|
||||
|
||||
已验证:
|
||||
|
||||
- 前端开发服务成功启动于 `http://localhost:1024`
|
||||
- 浏览器打开 `http://localhost:1024/login`,页面标题显示为“若依管理系统”
|
||||
- 浏览器点击登录后成功进入 `/index`
|
||||
- 页面已实际渲染“流程列表”业务页面
|
||||
- 通过前端代理访问 `http://localhost:1024/dev-api/captchaImage` 返回:
|
||||
- `{"msg":"操作成功","code":200,"captchaEnabled":false}`
|
||||
- 源码态后端在 `Java 8 + spring-boot:run` 模式下可启动成功
|
||||
- 登录接口可成功返回 token:
|
||||
- `POST /login`
|
||||
|
||||
## 联调中发现的遗留问题
|
||||
|
||||
浏览器进入系统后,页面出现两条后端错误提示:
|
||||
|
||||
1. 数据库缺少 `sys_notice_read` 表
|
||||
2. `GET /loanPricing/workflow/list` 仍返回 `TooManyResultsException`
|
||||
|
||||
其中第 1 项已确认当前仓库 SQL 中已有对应建表语句:
|
||||
|
||||
- `sql/ry_20260330.sql`
|
||||
- `sql/loan_pricing_prod_init_20260331.sql`
|
||||
|
||||
说明当前代码迁移已落地,但联调数据库尚未完全补齐到 `springboot2` 基线所需表结构。
|
||||
|
||||
第 2 项已经定位到当前运行态仍存在列表查询返回值与 MyBatis 映射结果不一致的问题,表现为:
|
||||
|
||||
- `GET /loanPricing/workflow/list?pageNum=1&pageSize=10`
|
||||
- 返回:`{"msg":"nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 15","code":500}`
|
||||
|
||||
## 结论
|
||||
|
||||
本次代码迁移已经完成:
|
||||
|
||||
- 框架层已切回 `RuoYi-Vue springboot2`
|
||||
- 业务模块和业务页面已接回
|
||||
- Java 8 / Spring Boot 2 / Vue 2 的编译与构建链路已打通
|
||||
- 浏览器已成功进入业务页面与登录链路
|
||||
|
||||
当前剩余问题集中在:
|
||||
|
||||
- 联调数据库尚未补齐 `sys_notice_read` 表
|
||||
- `workflow/list` 运行态查询仍需继续收口
|
||||
147
pom.xml
147
pom.xml
@@ -6,54 +6,106 @@
|
||||
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.9.1</version>
|
||||
<version>3.9.2</version>
|
||||
|
||||
<name>ruoyi</name>
|
||||
<url>http://www.ruoyi.vip</url>
|
||||
<description>若依管理系统</description>
|
||||
|
||||
<properties>
|
||||
<ruoyi.version>3.9.1</ruoyi.version>
|
||||
<ruoyi.version>3.9.2</ruoyi.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<mybatis-spring-boot.version>3.0.5</mybatis-spring-boot.version>
|
||||
<druid.version>1.2.27</druid.version>
|
||||
<spring-boot.version>2.5.15</spring-boot.version>
|
||||
<druid.version>1.2.28</druid.version>
|
||||
<yauaa.version>7.32.0</yauaa.version>
|
||||
<swagger.version>3.0.0</swagger.version>
|
||||
<kaptcha.version>2.3.3</kaptcha.version>
|
||||
<pagehelper.boot.version>2.1.1</pagehelper.boot.version>
|
||||
<fastjson.version>2.0.60</fastjson.version>
|
||||
<oshi.version>6.9.1</oshi.version>
|
||||
<pagehelper.boot.version>1.4.7</pagehelper.boot.version>
|
||||
<fastjson.version>2.0.61</fastjson.version>
|
||||
<oshi.version>6.10.0</oshi.version>
|
||||
<commons.io.version>2.21.0</commons.io.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<quartz.version>2.5.2</quartz.version>
|
||||
<mysql.version>8.2.0</mysql.version>
|
||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||
<jakarta.version>6.0.0</jakarta.version>
|
||||
<springdoc.version>2.8.14</springdoc.version>
|
||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||
<jsqlparser.version>4.5</jsqlparser.version>
|
||||
<!-- override dependency version -->
|
||||
<tomcat.version>9.0.112</tomcat.version>
|
||||
<logback.version>1.2.13</logback.version>
|
||||
<spring-security.version>5.7.14</spring-security.version>
|
||||
<spring-framework.version>5.3.39</spring-framework.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<!-- 覆盖SpringFramework的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>${spring-framework.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 覆盖SpringSecurity的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.5.8</version>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 覆盖logback的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 覆盖tomcat的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-el</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-websocket</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里数据库连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -71,30 +123,6 @@
|
||||
<version>${pagehelper.boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>${mybatis-spring-boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>${jakarta.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 获取系统信息 -->
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
@@ -102,11 +130,17 @@
|
||||
<version>${oshi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-doc -->
|
||||
<!-- Swagger3依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- io常用工具类 -->
|
||||
@@ -130,13 +164,6 @@
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 定时任务 -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>${quartz.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里JSON解析器 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
@@ -200,6 +227,18 @@
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>${jsqlparser.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -219,19 +258,13 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<parameters>true</parameters>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
BIN
ruoyi-admin/.DS_Store
vendored
BIN
ruoyi-admin/.DS_Store
vendored
Binary file not shown.
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.1</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
@@ -24,16 +24,23 @@
|
||||
<optional>true</optional> <!-- 表示依赖不会传递 -->
|
||||
</dependency>
|
||||
|
||||
<!-- spring-doc -->
|
||||
<!-- swagger3-->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Mysql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块-->
|
||||
@@ -60,12 +67,6 @@
|
||||
<artifactId>ruoyi-loan-pricing</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -73,9 +74,9 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.5.4</version>
|
||||
<version>2.5.15</version>
|
||||
<configuration>
|
||||
<addResources>true</addResources>
|
||||
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
||||
@@ -3,9 +3,9 @@ package com.ruoyi.web.controller.common;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jakarta.annotation.Resource;
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.ruoyi.web.controller.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@@ -7,6 +7,13 @@ import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.domain.SysCache;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -15,33 +22,24 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 缓存监控
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/cache")
|
||||
public class CacheController
|
||||
{
|
||||
private final RedisCache redisCache;
|
||||
|
||||
private final static List<SysCache> caches = new ArrayList<SysCache>();
|
||||
private static final List<SysCache> CACHES = new ArrayList<SysCache>();
|
||||
|
||||
static
|
||||
{
|
||||
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
|
||||
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
|
||||
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
|
||||
caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
|
||||
caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
|
||||
caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
|
||||
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
|
||||
CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
|
||||
CACHES.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
|
||||
CACHES.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
|
||||
CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
|
||||
CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
|
||||
CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
|
||||
CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
|
||||
}
|
||||
|
||||
public CacheController(RedisCache redisCache)
|
||||
@@ -55,23 +53,23 @@ public class CacheController
|
||||
{
|
||||
InMemoryCacheStats stats = redisCache.getCacheStats();
|
||||
Map<String, Object> info = new LinkedHashMap<>();
|
||||
info.put("cache_type", stats.cacheType());
|
||||
info.put("cache_mode", stats.mode());
|
||||
info.put("key_size", stats.keySize());
|
||||
info.put("hit_count", stats.hitCount());
|
||||
info.put("miss_count", stats.missCount());
|
||||
info.put("expired_count", stats.expiredCount());
|
||||
info.put("write_count", stats.writeCount());
|
||||
info.put("cache_type", stats.getCacheType());
|
||||
info.put("cache_mode", stats.getMode());
|
||||
info.put("key_size", stats.getKeySize());
|
||||
info.put("hit_count", stats.getHitCount());
|
||||
info.put("miss_count", stats.getMissCount());
|
||||
info.put("expired_count", stats.getExpiredCount());
|
||||
info.put("write_count", stats.getWriteCount());
|
||||
|
||||
Map<String, Object> result = new HashMap<>(3);
|
||||
result.put("info", info);
|
||||
result.put("dbSize", stats.keySize());
|
||||
result.put("dbSize", stats.getKeySize());
|
||||
|
||||
List<Map<String, String>> pieList = new ArrayList<>();
|
||||
pieList.add(statEntry("hit_count", stats.hitCount()));
|
||||
pieList.add(statEntry("miss_count", stats.missCount()));
|
||||
pieList.add(statEntry("expired_count", stats.expiredCount()));
|
||||
pieList.add(statEntry("write_count", stats.writeCount()));
|
||||
pieList.add(statEntry("hit_count", stats.getHitCount()));
|
||||
pieList.add(statEntry("miss_count", stats.getMissCount()));
|
||||
pieList.add(statEntry("expired_count", stats.getExpiredCount()));
|
||||
pieList.add(statEntry("write_count", stats.getWriteCount()));
|
||||
result.put("commandStats", pieList);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
@@ -80,7 +78,7 @@ public class CacheController
|
||||
@GetMapping("/getNames")
|
||||
public AjaxResult cache()
|
||||
{
|
||||
return AjaxResult.success(caches);
|
||||
return AjaxResult.success(CACHES);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
|
||||
@@ -139,9 +137,9 @@ public class CacheController
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
if (cacheValue instanceof String stringValue)
|
||||
if (cacheValue instanceof String)
|
||||
{
|
||||
return stringValue;
|
||||
return (String) cacheValue;
|
||||
}
|
||||
return JSON.toJSONString(cacheValue);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.monitor;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.monitor;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -110,6 +111,20 @@ public class SysDeptController extends BaseController
|
||||
return toAjax(deptService.updateDept(dept));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存部门排序
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
|
||||
@Log(title = "保存部门排序", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updateSort")
|
||||
public AjaxResult updateSort(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String[] deptIds = params.get("deptIds").split(",");
|
||||
String[] orderNums = params.get("orderNums").split(",");
|
||||
deptService.updateDeptSort(deptIds, orderNums);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* 首页
|
||||
@@ -18,6 +25,9 @@ public class SysIndexController
|
||||
@Autowired
|
||||
private RuoYiConfig ruoyiConfig;
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@@ -26,4 +36,29 @@ public class SysIndexController
|
||||
{
|
||||
return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁屏幕
|
||||
*/
|
||||
@PostMapping("/unlockscreen")
|
||||
public AjaxResult unlockScreen(@RequestBody Map<String, String> body)
|
||||
{
|
||||
String password = body.get("password");
|
||||
if (StringUtils.isEmpty(password))
|
||||
{
|
||||
return AjaxResult.error("密码不能为空");
|
||||
}
|
||||
String username = SecurityUtils.getUsername();
|
||||
SysUser user = userService.selectUserByUserName(username);
|
||||
if (user == null)
|
||||
{
|
||||
return AjaxResult.error("服务器超时,请重新登录");
|
||||
}
|
||||
if (!SecurityUtils.matchesPassword(password, user.getPassword()))
|
||||
{
|
||||
return AjaxResult.error("密码错误,请重新输入");
|
||||
}
|
||||
|
||||
return AjaxResult.success("解锁成功");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.SysLoginService;
|
||||
import com.ruoyi.framework.web.service.SysPermissionService;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
@@ -48,9 +47,6 @@ public class SysLoginController
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private PasswordTransferCryptoService passwordTransferCryptoService;
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
*
|
||||
@@ -61,7 +57,6 @@ public class SysLoginController
|
||||
public AjaxResult login(@RequestBody LoginBody loginBody)
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
loginBody.setPassword(passwordTransferCryptoService.decrypt(loginBody.getPassword()));
|
||||
// 生成令牌
|
||||
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
|
||||
loginBody.getUuid());
|
||||
@@ -69,22 +64,6 @@ public class SysLoginController
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
* 该方法处理用户登录请求,无需验证码
|
||||
* @param loginBody 登录信息,包含用户名和密码
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping("/login/test")
|
||||
public AjaxResult loginWithoutCaptcha (@RequestBody LoginBody loginBody)
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
// 生成令牌
|
||||
String token = loginService.loginWithoutCaptcha(loginBody.getUsername(), loginBody.getPassword());
|
||||
ajax.put(Constants.TOKEN, token);
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -129,6 +130,20 @@ public class SysMenuController extends BaseController
|
||||
return toAjax(menuService.updateMenu(menu));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存菜单排序
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
|
||||
@Log(title = "保存菜单排序", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updateSort")
|
||||
public AjaxResult updateSort(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String[] menuIds = params.get("menuIds").split(",");
|
||||
String[] orderNums = params.get("orderNums").split(",");
|
||||
menuService.updateMenuSort(menuIds, orderNums);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*/
|
||||
|
||||
@@ -11,13 +11,16 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.SysNotice;
|
||||
import com.ruoyi.system.service.ISysNoticeReadService;
|
||||
import com.ruoyi.system.service.ISysNoticeService;
|
||||
|
||||
/**
|
||||
@@ -32,6 +35,9 @@ public class SysNoticeController extends BaseController
|
||||
@Autowired
|
||||
private ISysNoticeService noticeService;
|
||||
|
||||
@Autowired
|
||||
private ISysNoticeReadService noticeReadService;
|
||||
|
||||
/**
|
||||
* 获取通知公告列表
|
||||
*/
|
||||
@@ -78,6 +84,46 @@ public class SysNoticeController extends BaseController
|
||||
return toAjax(noticeService.updateNotice(notice));
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页顶部公告列表(返回全部正常公告,带当前用户已读标记,最多5条)
|
||||
*/
|
||||
@GetMapping("/listTop")
|
||||
@ResponseBody
|
||||
public AjaxResult listTop()
|
||||
{
|
||||
Long userId = getUserId();
|
||||
List<SysNotice> list = noticeReadService.selectNoticeListWithReadStatus(userId, 5);
|
||||
long unreadCount = list.stream().filter(n -> !n.getIsRead()).count();
|
||||
AjaxResult result = AjaxResult.success(list);
|
||||
result.put("unreadCount", unreadCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记公告已读
|
||||
*/
|
||||
@PostMapping("/markRead")
|
||||
@ResponseBody
|
||||
public AjaxResult markRead(Long noticeId)
|
||||
{
|
||||
Long userId = getUserId();
|
||||
noticeReadService.markRead(noticeId, userId);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量标记已读
|
||||
*/
|
||||
@PostMapping("/markReadAll")
|
||||
@ResponseBody
|
||||
public AjaxResult markReadAll(String ids)
|
||||
{
|
||||
Long userId = getUserId();
|
||||
Long[] noticeIds = Convert.toLongArray(ids);
|
||||
noticeReadService.markReadBatch(userId, noticeIds);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知公告
|
||||
*/
|
||||
@@ -86,6 +132,7 @@ public class SysNoticeController extends BaseController
|
||||
@DeleteMapping("/{noticeIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] noticeIds)
|
||||
{
|
||||
noticeReadService.deleteByNoticeIds(noticeIds);
|
||||
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||
import com.ruoyi.common.utils.file.FileUtils;
|
||||
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
@@ -42,9 +41,6 @@ public class SysProfileController extends BaseController
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private PasswordTransferCryptoService passwordTransferCryptoService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
*/
|
||||
@@ -96,8 +92,8 @@ public class SysProfileController extends BaseController
|
||||
@PutMapping("/updatePwd")
|
||||
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String oldPassword = passwordTransferCryptoService.decrypt(params.get("oldPassword"));
|
||||
String newPassword = passwordTransferCryptoService.decrypt(params.get("newPassword"));
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = getLoginUser();
|
||||
Long userId = loginUser.getUserId();
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.domain.model.RegisterBody;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.SysRegisterService;
|
||||
import com.ruoyi.system.service.ISysConfigService;
|
||||
|
||||
@@ -26,9 +25,6 @@ public class SysRegisterController extends BaseController
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private PasswordTransferCryptoService passwordTransferCryptoService;
|
||||
|
||||
@PostMapping("/register")
|
||||
public AjaxResult register(@RequestBody RegisterBody user)
|
||||
{
|
||||
@@ -36,7 +32,6 @@ public class SysRegisterController extends BaseController
|
||||
{
|
||||
return error("当前系统没有开启注册功能!");
|
||||
}
|
||||
user.setPassword(passwordTransferCryptoService.decrypt(user.getPassword()));
|
||||
String msg = registerService.register(user);
|
||||
return StringUtils.isEmpty(msg) ? success() : error(msg);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -27,7 +27,6 @@ import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.system.service.ISysDeptService;
|
||||
import com.ruoyi.system.service.ISysPostService;
|
||||
import com.ruoyi.system.service.ISysRoleService;
|
||||
@@ -54,9 +53,6 @@ public class SysUserController extends BaseController
|
||||
@Autowired
|
||||
private ISysPostService postService;
|
||||
|
||||
@Autowired
|
||||
private PasswordTransferCryptoService passwordTransferCryptoService;
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
@@ -143,7 +139,6 @@ public class SysUserController extends BaseController
|
||||
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
user.setCreateBy(getUsername());
|
||||
user.setPassword(passwordTransferCryptoService.decrypt(user.getPassword()));
|
||||
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
|
||||
return toAjax(userService.insertUser(user));
|
||||
}
|
||||
@@ -201,7 +196,6 @@ public class SysUserController extends BaseController
|
||||
{
|
||||
userService.checkUserAllowed(user);
|
||||
userService.checkUserDataScope(user.getUserId());
|
||||
user.setPassword(passwordTransferCryptoService.decrypt(user.getPassword()));
|
||||
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
|
||||
user.setUpdateBy(getUsername());
|
||||
return toAjax(userService.resetPwd(user));
|
||||
|
||||
@@ -15,16 +15,19 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
||||
/**
|
||||
* swagger 用户测试方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Tag(name = "用户信息管理")
|
||||
@Api("用户信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/test/user")
|
||||
public class TestController extends BaseController
|
||||
@@ -35,7 +38,7 @@ public class TestController extends BaseController
|
||||
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户列表")
|
||||
@ApiOperation("获取用户列表")
|
||||
@GetMapping("/list")
|
||||
public R<List<UserEntity>> userList()
|
||||
{
|
||||
@@ -43,10 +46,10 @@ public class TestController extends BaseController
|
||||
return R.ok(userList);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户详细")
|
||||
@ApiOperation("获取用户详细")
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
|
||||
@GetMapping("/{userId}")
|
||||
public R<UserEntity> getUser(@PathVariable(name = "userId")
|
||||
Integer userId)
|
||||
public R<UserEntity> getUser(@PathVariable Integer userId)
|
||||
{
|
||||
if (!users.isEmpty() && users.containsKey(userId))
|
||||
{
|
||||
@@ -58,7 +61,13 @@ public class TestController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "新增用户")
|
||||
@ApiOperation("新增用户")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
|
||||
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
|
||||
})
|
||||
@PostMapping("/save")
|
||||
public R<String> save(UserEntity user)
|
||||
{
|
||||
@@ -70,10 +79,9 @@ public class TestController extends BaseController
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "更新用户")
|
||||
@ApiOperation("更新用户")
|
||||
@PutMapping("/update")
|
||||
public R<String> update(@RequestBody
|
||||
UserEntity user)
|
||||
public R<String> update(@RequestBody UserEntity user)
|
||||
{
|
||||
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
|
||||
{
|
||||
@@ -88,10 +96,10 @@ public class TestController extends BaseController
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户信息")
|
||||
@ApiOperation("删除用户信息")
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
|
||||
@DeleteMapping("/{userId}")
|
||||
public R<String> delete(@PathVariable(name = "userId")
|
||||
Integer userId)
|
||||
public R<String> delete(@PathVariable Integer userId)
|
||||
{
|
||||
if (!users.isEmpty() && users.containsKey(userId))
|
||||
{
|
||||
@@ -105,19 +113,19 @@ public class TestController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "用户实体")
|
||||
@ApiModel(value = "UserEntity", description = "用户实体")
|
||||
class UserEntity
|
||||
{
|
||||
@Schema(title = "用户ID")
|
||||
@ApiModelProperty("用户ID")
|
||||
private Integer userId;
|
||||
|
||||
@Schema(title = "用户名称")
|
||||
@ApiModelProperty("用户名称")
|
||||
private String username;
|
||||
|
||||
@Schema(title = "用户密码")
|
||||
@ApiModelProperty("用户密码")
|
||||
private String password;
|
||||
|
||||
@Schema(title = "用户手机")
|
||||
@ApiModelProperty("用户手机")
|
||||
private String mobile;
|
||||
|
||||
public UserEntity()
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
package com.ruoyi.web.core.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.models.auth.In;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ApiKey;
|
||||
import springfox.documentation.service.AuthorizationScope;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.SecurityReference;
|
||||
import springfox.documentation.service.SecurityScheme;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
/**
|
||||
* Swagger2的接口配置
|
||||
@@ -23,42 +34,92 @@ public class SwaggerConfig
|
||||
@Autowired
|
||||
private RuoYiConfig ruoyiConfig;
|
||||
|
||||
/** 是否开启swagger */
|
||||
@Value("${swagger.enabled}")
|
||||
private boolean enabled;
|
||||
|
||||
/** 设置请求的统一前缀 */
|
||||
@Value("${swagger.pathMapping}")
|
||||
private String pathMapping;
|
||||
|
||||
/**
|
||||
* 自定义的 OpenAPI 对象
|
||||
* 创建API
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPI customOpenApi()
|
||||
public Docket createRestApi()
|
||||
{
|
||||
return new OpenAPI().components(new Components()
|
||||
// 设置认证的请求头
|
||||
.addSecuritySchemes("apikey", securityScheme()))
|
||||
.addSecurityItem(new SecurityRequirement().addList("apikey"))
|
||||
.info(getApiInfo());
|
||||
return new Docket(DocumentationType.OAS_30)
|
||||
// 是否启用Swagger
|
||||
.enable(enabled)
|
||||
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
|
||||
.apiInfo(apiInfo())
|
||||
// 设置哪些接口暴露给Swagger展示
|
||||
.select()
|
||||
// 扫描所有有注解的api,用这种方式更灵活
|
||||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
// 扫描指定包中的swagger注解
|
||||
// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
|
||||
// 扫描所有 .apis(RequestHandlerSelectors.any())
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
/* 设置安全模式,swagger可以设置访问token */
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts())
|
||||
.pathMapping(pathMapping);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityScheme securityScheme()
|
||||
/**
|
||||
* 安全模式,这里指定token通过Authorization头请求头传递
|
||||
*/
|
||||
private List<SecurityScheme> securitySchemes()
|
||||
{
|
||||
return new SecurityScheme()
|
||||
.type(SecurityScheme.Type.APIKEY)
|
||||
.name("Authorization")
|
||||
.in(SecurityScheme.In.HEADER)
|
||||
.scheme("Bearer");
|
||||
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
|
||||
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
|
||||
return apiKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全上下文
|
||||
*/
|
||||
private List<SecurityContext> securityContexts()
|
||||
{
|
||||
List<SecurityContext> securityContexts = new ArrayList<>();
|
||||
securityContexts.add(
|
||||
SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
|
||||
.build());
|
||||
return securityContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的安全上引用
|
||||
*/
|
||||
private List<SecurityReference> defaultAuth()
|
||||
{
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
List<SecurityReference> securityReferences = new ArrayList<>();
|
||||
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
|
||||
return securityReferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加摘要信息
|
||||
*/
|
||||
public Info getApiInfo()
|
||||
private ApiInfo apiInfo()
|
||||
{
|
||||
return new Info()
|
||||
// 用ApiInfoBuilder进行定制
|
||||
return new ApiInfoBuilder()
|
||||
// 设置标题
|
||||
.title("标题:若依管理系统_接口文档")
|
||||
// 描述
|
||||
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
|
||||
// 作者信息
|
||||
.contact(new Contact().name(ruoyiConfig.getName()))
|
||||
.contact(new Contact(ruoyiConfig.getName(), null, null))
|
||||
// 版本
|
||||
.version("版本号:" + ruoyiConfig.getVersion());
|
||||
.version("版本号:" + ruoyiConfig.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
61
ruoyi-admin/src/main/resources/application-druid.yml
Normal file
61
ruoyi-admin/src/main/resources/application-druid.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: password
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: ruoyi
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
@@ -3,7 +3,7 @@ ruoyi:
|
||||
# 名称
|
||||
name: RuoYi
|
||||
# 版本
|
||||
version: 3.9.1
|
||||
version: 3.9.2
|
||||
# 版权年份
|
||||
copyrightYear: 2026
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
@@ -49,6 +49,19 @@ spring:
|
||||
restart:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 0
|
||||
password:
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
min-idle: 0
|
||||
max-idle: 8
|
||||
max-active: 8
|
||||
max-wait: -1ms
|
||||
|
||||
|
||||
# token配置
|
||||
@@ -60,8 +73,8 @@ token:
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
# MyBatis Plus配置
|
||||
mybatis-plus:
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.ruoyi.**.domain
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
@@ -75,14 +88,12 @@ pagehelper:
|
||||
supportMethodsArguments: true
|
||||
params: count=countSql
|
||||
|
||||
# Springdoc配置
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
# Swagger配置
|
||||
swagger:
|
||||
# 是否开启swagger
|
||||
enabled: true
|
||||
path: /swagger-ui.html
|
||||
tags-sorter: alpha
|
||||
# 请求前缀
|
||||
pathMapping: /dev-api
|
||||
|
||||
# 防盗链配置
|
||||
referer:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
window.onload = function() {
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "/v3/api-docs/default",
|
||||
configUrl: "/v3/api-docs/swagger-config",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout",
|
||||
tagsSorter: "alpha",
|
||||
validatorUrl: "",
|
||||
persistAuthorization: true
|
||||
});
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.ruoyi.web.controller.monitor;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
|
||||
class CacheControllerTest
|
||||
{
|
||||
@Test
|
||||
void shouldReturnInMemoryCacheSummary() throws Exception
|
||||
{
|
||||
RedisCache redisCache = new RedisCache(new InMemoryCacheStore());
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CacheController(redisCache)).build();
|
||||
|
||||
mockMvc.perform(get("/monitor/cache"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.info.cache_type").value("IN_MEMORY"))
|
||||
.andExpect(jsonPath("$.data.info.cache_mode").value("single-instance"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClearCacheKeysByPrefix() throws Exception
|
||||
{
|
||||
RedisCache redisCache = new RedisCache(new InMemoryCacheStore());
|
||||
redisCache.setCacheObject("login_tokens:a", "A");
|
||||
redisCache.setCacheObject("login_tokens:b", "B");
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CacheController(redisCache)).build();
|
||||
|
||||
mockMvc.perform(delete("/monitor/cache/clearCacheName/login_tokens:"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
mockMvc.perform(get("/monitor/cache/getKeys/login_tokens:"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data").isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.SysLoginService;
|
||||
|
||||
class SysLoginControllerPasswordTransferTest
|
||||
{
|
||||
@Test
|
||||
void shouldDecryptPasswordBeforeCallingLoginService() throws Exception
|
||||
{
|
||||
SysLoginService loginService = mock(SysLoginService.class);
|
||||
PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
|
||||
when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("admin123");
|
||||
when(loginService.login("admin", "admin123", "1", "u")).thenReturn("token");
|
||||
|
||||
SysLoginController controller = new SysLoginController();
|
||||
ReflectionTestUtils.setField(controller, "loginService", loginService);
|
||||
ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
mockMvc.perform(post("/login")
|
||||
.contentType("application/json")
|
||||
.content("{\"username\":\"admin\",\"password\":\"cipher\",\"code\":\"1\",\"uuid\":\"u\"}"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(passwordTransferCryptoService).decrypt("cipher");
|
||||
verify(loginService).login("admin", "admin123", "1", "u");
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
class SysProfileControllerPasswordTransferTest
|
||||
{
|
||||
@AfterEach
|
||||
void tearDown()
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDecryptPasswordsBeforeCheckingOldPassword() throws Exception
|
||||
{
|
||||
ISysUserService userService = mock(ISysUserService.class);
|
||||
TokenService tokenService = mock(TokenService.class);
|
||||
PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
|
||||
when(passwordTransferCryptoService.decrypt("oldCipher")).thenReturn("oldPlain");
|
||||
when(passwordTransferCryptoService.decrypt("newCipher")).thenReturn("newPlain");
|
||||
when(userService.resetUserPwd(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString()))
|
||||
.thenReturn(1);
|
||||
|
||||
SysUser storedUser = new SysUser();
|
||||
storedUser.setUserId(2L);
|
||||
storedUser.setPassword(SecurityUtils.encryptPassword("oldPlain"));
|
||||
when(userService.selectUserById(2L)).thenReturn(storedUser);
|
||||
|
||||
SysUser currentUser = new SysUser();
|
||||
currentUser.setUserId(2L);
|
||||
currentUser.setUserName("admin");
|
||||
LoginUser loginUser = new LoginUser(2L, 1L, currentUser, Collections.emptySet());
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList()));
|
||||
|
||||
SysProfileController controller = new SysProfileController();
|
||||
ReflectionTestUtils.setField(controller, "userService", userService);
|
||||
ReflectionTestUtils.setField(controller, "tokenService", tokenService);
|
||||
ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
mockMvc.perform(put("/system/user/profile/updatePwd")
|
||||
.contentType("application/json")
|
||||
.content("{\"oldPassword\":\"oldCipher\",\"newPassword\":\"newCipher\"}"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(passwordTransferCryptoService).decrypt("oldCipher");
|
||||
verify(passwordTransferCryptoService).decrypt("newCipher");
|
||||
verify(userService).resetUserPwd(org.mockito.ArgumentMatchers.eq(2L), org.mockito.ArgumentMatchers.anyString());
|
||||
verify(tokenService).setLoginUser(loginUser);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import com.ruoyi.common.core.domain.model.RegisterBody;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.framework.web.service.SysRegisterService;
|
||||
import com.ruoyi.system.service.ISysConfigService;
|
||||
|
||||
class SysRegisterControllerPasswordTransferTest
|
||||
{
|
||||
@Test
|
||||
void shouldDecryptPasswordBeforeCallingRegisterService() throws Exception
|
||||
{
|
||||
SysRegisterService registerService = mock(SysRegisterService.class);
|
||||
ISysConfigService configService = mock(ISysConfigService.class);
|
||||
PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
|
||||
when(configService.selectConfigByKey("sys.account.registerUser")).thenReturn("true");
|
||||
when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("admin123");
|
||||
when(registerService.register(any(RegisterBody.class))).thenReturn("");
|
||||
|
||||
SysRegisterController controller = new SysRegisterController();
|
||||
ReflectionTestUtils.setField(controller, "registerService", registerService);
|
||||
ReflectionTestUtils.setField(controller, "configService", configService);
|
||||
ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
mockMvc.perform(post("/register")
|
||||
.contentType("application/json")
|
||||
.content("{\"username\":\"u1\",\"password\":\"cipher\",\"code\":\"1\",\"uuid\":\"u\"}"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(passwordTransferCryptoService).decrypt("cipher");
|
||||
ArgumentCaptor<RegisterBody> captor = ArgumentCaptor.forClass(RegisterBody.class);
|
||||
verify(registerService).register(captor.capture());
|
||||
assertEquals("admin123", captor.getValue().getPassword());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.web.service.PasswordTransferCryptoService;
|
||||
import com.ruoyi.system.service.ISysDeptService;
|
||||
import com.ruoyi.system.service.ISysPostService;
|
||||
import com.ruoyi.system.service.ISysRoleService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
class SysUserControllerPasswordTransferTest
|
||||
{
|
||||
@AfterEach
|
||||
void tearDown()
|
||||
{
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDecryptPasswordBeforeAddingUser() throws Exception
|
||||
{
|
||||
ISysUserService userService = mock(ISysUserService.class);
|
||||
ISysRoleService roleService = mock(ISysRoleService.class);
|
||||
ISysDeptService deptService = mock(ISysDeptService.class);
|
||||
ISysPostService postService = mock(ISysPostService.class);
|
||||
PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
|
||||
when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("initPwd");
|
||||
when(userService.checkUserNameUnique(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(true);
|
||||
when(userService.insertUser(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(1);
|
||||
|
||||
setAuthentication();
|
||||
|
||||
SysUserController controller = new SysUserController();
|
||||
ReflectionTestUtils.setField(controller, "userService", userService);
|
||||
ReflectionTestUtils.setField(controller, "roleService", roleService);
|
||||
ReflectionTestUtils.setField(controller, "deptService", deptService);
|
||||
ReflectionTestUtils.setField(controller, "postService", postService);
|
||||
ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
mockMvc.perform(post("/system/user")
|
||||
.contentType("application/json")
|
||||
.content("{\"userName\":\"u1\",\"nickName\":\"n1\",\"deptId\":1,\"password\":\"cipher\"}"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(passwordTransferCryptoService).decrypt("cipher");
|
||||
ArgumentCaptor<SysUser> captor = ArgumentCaptor.forClass(SysUser.class);
|
||||
verify(userService).insertUser(captor.capture());
|
||||
assertTrue(SecurityUtils.matchesPassword("initPwd", captor.getValue().getPassword()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDecryptPasswordBeforeResettingUserPassword() throws Exception
|
||||
{
|
||||
ISysUserService userService = mock(ISysUserService.class);
|
||||
ISysRoleService roleService = mock(ISysRoleService.class);
|
||||
ISysDeptService deptService = mock(ISysDeptService.class);
|
||||
ISysPostService postService = mock(ISysPostService.class);
|
||||
PasswordTransferCryptoService passwordTransferCryptoService = mock(PasswordTransferCryptoService.class);
|
||||
when(passwordTransferCryptoService.decrypt("cipher")).thenReturn("resetPwd");
|
||||
when(userService.resetPwd(org.mockito.ArgumentMatchers.any(SysUser.class))).thenReturn(1);
|
||||
|
||||
setAuthentication();
|
||||
|
||||
SysUserController controller = new SysUserController();
|
||||
ReflectionTestUtils.setField(controller, "userService", userService);
|
||||
ReflectionTestUtils.setField(controller, "roleService", roleService);
|
||||
ReflectionTestUtils.setField(controller, "deptService", deptService);
|
||||
ReflectionTestUtils.setField(controller, "postService", postService);
|
||||
ReflectionTestUtils.setField(controller, "passwordTransferCryptoService", passwordTransferCryptoService);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
|
||||
mockMvc.perform(put("/system/user/resetPwd")
|
||||
.contentType("application/json")
|
||||
.content("{\"userId\":2,\"password\":\"cipher\"}"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
verify(passwordTransferCryptoService).decrypt("cipher");
|
||||
ArgumentCaptor<SysUser> captor = ArgumentCaptor.forClass(SysUser.class);
|
||||
verify(userService).resetPwd(captor.capture());
|
||||
assertTrue(SecurityUtils.matchesPassword("resetPwd", captor.getValue().getPassword()));
|
||||
}
|
||||
|
||||
private void setAuthentication()
|
||||
{
|
||||
SysUser currentUser = new SysUser();
|
||||
currentUser.setUserId(1L);
|
||||
currentUser.setUserName("admin");
|
||||
LoginUser loginUser = new LoginUser(1L, 1L, currentUser, Collections.emptySet());
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.1</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -89,6 +89,18 @@
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- redis 缓存操作 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- pool 对象池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
@@ -97,32 +109,8 @@
|
||||
|
||||
<!-- servlet包 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<!-- ruoyi-springboot3 / mybatis-plus 配置 -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
<version>3.5.16</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>3.5.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||
<version>3.5.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -16,15 +16,25 @@ import java.lang.annotation.Target;
|
||||
@Documented
|
||||
public @interface DataScope
|
||||
{
|
||||
/**
|
||||
* 用户表的别名
|
||||
*/
|
||||
public String userAlias() default "";
|
||||
|
||||
/**
|
||||
* 部门表的别名
|
||||
*/
|
||||
public String deptAlias() default "";
|
||||
|
||||
/**
|
||||
* 用户表的别名
|
||||
* 用户字段名
|
||||
*/
|
||||
public String userAlias() default "";
|
||||
public String userField() default "user_id";
|
||||
|
||||
/**
|
||||
* 部门字段名
|
||||
*/
|
||||
public String deptField() default "dept_id";
|
||||
|
||||
/**
|
||||
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
|
||||
|
||||
@@ -56,7 +56,6 @@ public @interface Excel
|
||||
/**
|
||||
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
|
||||
|
||||
/**
|
||||
|
||||
@@ -170,4 +170,35 @@ public class Constants
|
||||
*/
|
||||
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||
"org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
|
||||
|
||||
/**
|
||||
* 部门相关常量
|
||||
*/
|
||||
public static class Dept
|
||||
{
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
|
||||
/**
|
||||
* 部门数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT = "3";
|
||||
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_SELF = "5";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ public class GenConstants
|
||||
/** 上级菜单名称字段 */
|
||||
public static final String PARENT_MENU_NAME = "parentMenuName";
|
||||
|
||||
/** 生成详情页开关 */
|
||||
public static final String GEN_VIEW = "genView";
|
||||
|
||||
/** 数据库字符串类型 */
|
||||
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
|
||||
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
record InMemoryCacheEntry(Object value, Long expireAtMillis)
|
||||
public class InMemoryCacheEntry
|
||||
{
|
||||
boolean isExpired(long now)
|
||||
private final Object value;
|
||||
private final Long expireAtMillis;
|
||||
|
||||
public InMemoryCacheEntry(Object value, Long expireAtMillis)
|
||||
{
|
||||
this.value = value;
|
||||
this.expireAtMillis = expireAtMillis;
|
||||
}
|
||||
|
||||
public Object getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public Long getExpireAtMillis()
|
||||
{
|
||||
return expireAtMillis;
|
||||
}
|
||||
|
||||
public boolean isExpired(long now)
|
||||
{
|
||||
return expireAtMillis != null && expireAtMillis <= now;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,58 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
public record InMemoryCacheStats(
|
||||
String cacheType,
|
||||
String mode,
|
||||
long keySize,
|
||||
long hitCount,
|
||||
long missCount,
|
||||
long expiredCount,
|
||||
long writeCount)
|
||||
public class InMemoryCacheStats
|
||||
{
|
||||
private final String cacheType;
|
||||
private final String mode;
|
||||
private final long keySize;
|
||||
private final long hitCount;
|
||||
private final long missCount;
|
||||
private final long expiredCount;
|
||||
private final long writeCount;
|
||||
|
||||
public InMemoryCacheStats(String cacheType, String mode, long keySize, long hitCount, long missCount, long expiredCount, long writeCount)
|
||||
{
|
||||
this.cacheType = cacheType;
|
||||
this.mode = mode;
|
||||
this.keySize = keySize;
|
||||
this.hitCount = hitCount;
|
||||
this.missCount = missCount;
|
||||
this.expiredCount = expiredCount;
|
||||
this.writeCount = writeCount;
|
||||
}
|
||||
|
||||
public String getCacheType()
|
||||
{
|
||||
return cacheType;
|
||||
}
|
||||
|
||||
public String getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
public long getKeySize()
|
||||
{
|
||||
return keySize;
|
||||
}
|
||||
|
||||
public long getHitCount()
|
||||
{
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
public long getMissCount()
|
||||
{
|
||||
return missCount;
|
||||
}
|
||||
|
||||
public long getExpiredCount()
|
||||
{
|
||||
return expiredCount;
|
||||
}
|
||||
|
||||
public long getWriteCount()
|
||||
{
|
||||
return writeCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -10,7 +14,6 @@ import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class InMemoryCacheStore
|
||||
@@ -36,7 +39,7 @@ public class InMemoryCacheStore
|
||||
public <T> T get(String key)
|
||||
{
|
||||
InMemoryCacheEntry entry = readEntry(key);
|
||||
return entry == null ? null : (T) entry.value();
|
||||
return entry == null ? null : (T) entry.getValue();
|
||||
}
|
||||
|
||||
public boolean hasKey(String key)
|
||||
@@ -63,12 +66,13 @@ public class InMemoryCacheStore
|
||||
{
|
||||
purgeExpiredEntries();
|
||||
Set<String> matchedKeys = new TreeSet<>();
|
||||
entries.forEach((key, value) -> {
|
||||
if (matches(pattern, key))
|
||||
for (Map.Entry<String, InMemoryCacheEntry> entry : entries.entrySet())
|
||||
{
|
||||
matchedKeys.add(key);
|
||||
if (matches(pattern, entry.getKey()))
|
||||
{
|
||||
matchedKeys.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
});
|
||||
return matchedKeys;
|
||||
}
|
||||
|
||||
@@ -76,9 +80,24 @@ public class InMemoryCacheStore
|
||||
{
|
||||
Objects.requireNonNull(unit, "TimeUnit must not be null");
|
||||
long expireAtMillis = System.currentTimeMillis() + Math.max(0L, unit.toMillis(timeout));
|
||||
return entries.computeIfPresent(key, (cacheKey, entry) -> entry.isExpired(System.currentTimeMillis())
|
||||
? null
|
||||
: new InMemoryCacheEntry(entry.value(), expireAtMillis)) != null;
|
||||
while (true)
|
||||
{
|
||||
InMemoryCacheEntry currentEntry = entries.get(key);
|
||||
if (currentEntry == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (currentEntry.isExpired(System.currentTimeMillis()))
|
||||
{
|
||||
removeExpiredEntry(key, currentEntry);
|
||||
return false;
|
||||
}
|
||||
InMemoryCacheEntry nextEntry = new InMemoryCacheEntry(currentEntry.getValue(), expireAtMillis);
|
||||
if (entries.replace(key, currentEntry, nextEntry))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getExpire(String key)
|
||||
@@ -93,11 +112,11 @@ public class InMemoryCacheStore
|
||||
{
|
||||
return -2L;
|
||||
}
|
||||
if (entry.expireAtMillis() == null)
|
||||
if (entry.getExpireAtMillis() == null)
|
||||
{
|
||||
return -1L;
|
||||
}
|
||||
long remainingMillis = Math.max(0L, entry.expireAtMillis() - System.currentTimeMillis());
|
||||
long remainingMillis = Math.max(0L, entry.getExpireAtMillis() - System.currentTimeMillis());
|
||||
long unitMillis = Math.max(1L, unit.toMillis(1));
|
||||
return (remainingMillis + unitMillis - 1) / unitMillis;
|
||||
}
|
||||
@@ -109,10 +128,10 @@ public class InMemoryCacheStore
|
||||
entries.compute(key, (cacheKey, currentEntry) -> {
|
||||
long now = System.currentTimeMillis();
|
||||
boolean missingOrExpired = currentEntry == null || currentEntry.isExpired(now);
|
||||
long nextValue = missingOrExpired ? 1L : toLong(currentEntry.value()) + 1L;
|
||||
Long expireAtMillis = missingOrExpired || currentEntry.expireAtMillis() == null
|
||||
long nextValue = missingOrExpired ? 1L : toLong(currentEntry.getValue()) + 1L;
|
||||
Long expireAtMillis = (missingOrExpired || currentEntry.getExpireAtMillis() == null)
|
||||
? now + Math.max(0L, unit.toMillis(timeout))
|
||||
: currentEntry.expireAtMillis();
|
||||
: currentEntry.getExpireAtMillis();
|
||||
if (missingOrExpired && currentEntry != null)
|
||||
{
|
||||
expiredCount.incrementAndGet();
|
||||
@@ -142,6 +161,7 @@ public class InMemoryCacheStore
|
||||
writeCount.get());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Map<String, T> getMap(String key)
|
||||
{
|
||||
Map<String, T> value = get(key);
|
||||
@@ -178,6 +198,7 @@ public class InMemoryCacheStore
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Set<T> getSet(String key)
|
||||
{
|
||||
Set<T> value = get(key);
|
||||
@@ -189,15 +210,28 @@ public class InMemoryCacheStore
|
||||
set(key, new HashSet<>(dataSet));
|
||||
}
|
||||
|
||||
public <T> java.util.List<T> getList(String key)
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> getList(String key)
|
||||
{
|
||||
java.util.List<T> value = get(key);
|
||||
return value == null ? null : new java.util.ArrayList<>(value);
|
||||
List<T> value = get(key);
|
||||
return value == null ? null : new ArrayList<>(value);
|
||||
}
|
||||
|
||||
public <T> void putList(String key, java.util.List<T> dataList)
|
||||
public <T> void putList(String key, List<T> dataList)
|
||||
{
|
||||
set(key, new java.util.ArrayList<>(dataList));
|
||||
set(key, new ArrayList<>(dataList));
|
||||
}
|
||||
|
||||
private void setWithOptionalTtl(String key, Object value, long ttlMillis)
|
||||
{
|
||||
if (ttlMillis > 0)
|
||||
{
|
||||
set(key, value, ttlMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void putEntry(String key, InMemoryCacheEntry entry)
|
||||
@@ -227,12 +261,13 @@ public class InMemoryCacheStore
|
||||
private void purgeExpiredEntries()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
entries.forEach((key, entry) -> {
|
||||
if (entry.isExpired(now))
|
||||
for (Map.Entry<String, InMemoryCacheEntry> entry : entries.entrySet())
|
||||
{
|
||||
removeExpiredEntry(key, entry);
|
||||
if (entry.getValue().isExpired(now))
|
||||
{
|
||||
removeExpiredEntry(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeExpiredEntry(String key, InMemoryCacheEntry expectedEntry)
|
||||
@@ -258,20 +293,10 @@ public class InMemoryCacheStore
|
||||
|
||||
private long toLong(Object value)
|
||||
{
|
||||
if (value instanceof Number number)
|
||||
if (value instanceof Number)
|
||||
{
|
||||
return number.longValue();
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return Long.parseLong(String.valueOf(value));
|
||||
}
|
||||
|
||||
private void setWithOptionalTtl(String key, Object value, long ttlMillis)
|
||||
{
|
||||
if (ttlMillis > 0)
|
||||
{
|
||||
set(key, value, ttlMillis, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import java.util.Set;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
|
||||
@@ -2,9 +2,10 @@ package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.*;
|
||||
import javax.validation.constraints.*;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.annotation.Excel.ColumnType;
|
||||
@@ -69,6 +70,7 @@ public class SysUser extends BaseEntity
|
||||
private String loginIp;
|
||||
|
||||
/** 最后登录时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
|
||||
private Date loginDate;
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStats;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -7,15 +11,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStats;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
|
||||
/**
|
||||
* 本地缓存门面,保留原有 RedisCache 业务入口。
|
||||
*
|
||||
* @author ruoyi
|
||||
**/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Component
|
||||
public class RedisCache
|
||||
|
||||
@@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 防盗链过滤器
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.ruoyi.common.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import com.ruoyi.common.utils.http.HttpHelper;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.enums.HttpMethod;
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ package com.ruoyi.common.filter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
{
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
@@ -41,14 +41,10 @@ public class DictUtils
|
||||
*/
|
||||
public static List<SysDictData> getDictCache(String key)
|
||||
{
|
||||
Object cacheObject = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (cacheObject instanceof List<?> listCache)
|
||||
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNotNull(arrayCache))
|
||||
{
|
||||
return (List<SysDictData>) listCache;
|
||||
}
|
||||
if (StringUtils.isNotNull(cacheObject))
|
||||
{
|
||||
return JSON.parseArray(JSON.toJSONString(cacheObject), SysDictData.class);
|
||||
return arrayCache.toList(SysDictData.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.ruoyi.common.core.text.StrFormatter;
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
{
|
||||
/** 空字符串 */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.ruoyi.common.utils.bean;
|
||||
|
||||
import java.util.Set;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Validator;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
|
||||
/**
|
||||
* bean对象属性验证
|
||||
|
||||
@@ -9,8 +9,8 @@ import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import javax.servlet.ServletRequest;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -11,8 +11,6 @@ import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
@@ -23,10 +21,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* 通用http发送方法
|
||||
@@ -295,79 +290,4 @@ public class HttpUtils
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 POST 请求(application/x-www-form-urlencoded 格式)
|
||||
* @param url 请求地址
|
||||
* @param params 表单参数
|
||||
* @param headers 请求头(可为 null)
|
||||
* @param responseType 响应类型
|
||||
* @param <T> 泛型返回值
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T> T doPostFormUrlEncoded(String url, Map<String, String> params, HttpHeaders headers, Class<T> responseType) {
|
||||
// 构建表单参数
|
||||
MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
|
||||
if (params != null && !params.isEmpty()) {
|
||||
formParams.setAll(params);
|
||||
}
|
||||
|
||||
// 构建请求头(指定 Content-Type)
|
||||
HttpHeaders requestHeaders = headers == null ? new HttpHeaders() : headers;
|
||||
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
requestHeaders.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
|
||||
|
||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formParams, requestHeaders);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
// 发送请求
|
||||
try {
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
log.info("---------------------->POST(form-urlencoded) 请求成功,URL:{},响应结果:{}", url, response.getBody());
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("POST(form-urlencoded) 请求失败,URL:" + url + ",异常信息:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 新增:JSON 格式 POST 请求 ==========
|
||||
/**
|
||||
* 发送 POST 请求(application/json 格式)
|
||||
* @param url 请求地址
|
||||
* @param requestBody 请求体(Java 对象,自动序列化为 JSON)
|
||||
* @param headers 请求头(可为 null,默认已设置 Content-Type: application/json)
|
||||
* @param responseType 响应类型
|
||||
* @param <T> 响应泛型类型
|
||||
* @param <R> 请求体类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T, R> T doPostJson(String url, R requestBody, HttpHeaders headers, Class<T> responseType) {
|
||||
// 构建请求头,默认设置 JSON 格式
|
||||
HttpHeaders requestHeaders = headers == null ? new HttpHeaders() : headers;
|
||||
// 确保 Content-Type 为 JSON,避免外部传入覆盖
|
||||
if (!requestHeaders.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||
requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||
}
|
||||
|
||||
// 构建请求实体(请求体 + 请求头)
|
||||
HttpEntity<R> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
// 发送 JSON POST 请求
|
||||
try {
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
log.info("---------------------->POST(JSON) 请求成功,URL:{},响应结果:{}", url, response.getBody());
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("POST(JSON) 请求失败,URL:" + url + ",异常信息:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class AddressUtils
|
||||
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
|
||||
|
||||
// IP地址查询
|
||||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||
public static final String IP_URL = "https://whois.pconline.com.cn/ipJson.jsp";
|
||||
|
||||
// 未知地址
|
||||
public static final String UNKNOWN = "XX XX";
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.ruoyi.common.utils.ip;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.ruoyi.common.utils.poi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 多 Sheet 导出时的数据信息
|
||||
*
|
||||
* 使用示例:
|
||||
* <pre>
|
||||
* List<ExcelSheet<?>> sheets = new ArrayList<>();
|
||||
* sheets.add(new ExcelSheet<>("参数数据", configList, Config.class, "参数信息"));
|
||||
* sheets.add(new ExcelSheet<>("岗位数据", postList, Post.class, "岗位信息"));
|
||||
* return ExcelUtil.exportMultiSheet(sheets);
|
||||
* </pre>
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ExcelSheet<T>
|
||||
{
|
||||
/** Sheet 名称 */
|
||||
private String sheetName;
|
||||
|
||||
/** 导出数据集合 */
|
||||
private List<T> list;
|
||||
|
||||
/** 数据对应的实体 Class */
|
||||
private Class<T> clazz;
|
||||
|
||||
/** Sheet 顶部大标题(可为空) */
|
||||
private String title;
|
||||
|
||||
public ExcelSheet(String sheetName, List<T> list, Class<T> clazz)
|
||||
{
|
||||
this(sheetName, list, clazz, "");
|
||||
}
|
||||
|
||||
public ExcelSheet(String sheetName, List<T> list, Class<T> clazz, String title)
|
||||
{
|
||||
this.sheetName = sheetName;
|
||||
this.list = list != null ? list : new ArrayList<>();
|
||||
this.clazz = clazz;
|
||||
this.title = title != null ? title : "";
|
||||
}
|
||||
|
||||
public String getSheetName()
|
||||
{
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
public List<T> getList()
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
public Class<T> getClazz()
|
||||
{
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setSheetName(String sheetName)
|
||||
{
|
||||
this.sheetName = sheetName;
|
||||
}
|
||||
|
||||
public void setList(List<T> list)
|
||||
{
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public void setClazz(Class<T> clazz)
|
||||
{
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public void setTitle(String title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
@@ -106,6 +106,11 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
public Map<String, String> sysDictMap = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* 单元格样式缓存
|
||||
*/
|
||||
private Map<String, CellStyle> cellStyleCache = new HashMap<String, CellStyle>();
|
||||
|
||||
/**
|
||||
* Excel sheet最大行数,默认65536
|
||||
*/
|
||||
@@ -418,7 +423,7 @@ public class ExcelUtil<T>
|
||||
Object val = this.getCellValue(row, entry.getKey());
|
||||
|
||||
// 如果不存在实例则新建.
|
||||
entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity);
|
||||
entity = (entity == null ? clazz.newInstance() : entity);
|
||||
// 从map中得到对应列的field.
|
||||
Field field = (Field) entry.getValue()[0];
|
||||
Excel attr = (Excel) entry.getValue()[1];
|
||||
@@ -580,6 +585,117 @@ public class ExcelUtil<T>
|
||||
exportExcel(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多 Sheet 导出 —— 将多个不同类型的数据集合写入同一 Excel,直接输出到 HttpServletResponse
|
||||
*
|
||||
* @param response HTTP 响应
|
||||
* @param sheets Sheet 描述列表
|
||||
*/
|
||||
public static void exportMultiSheet(HttpServletResponse response, List<ExcelSheet<?>> sheets)
|
||||
{
|
||||
if (sheets == null || sheets.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SXSSFWorkbook wb = buildWorkbook(sheets);
|
||||
try
|
||||
{
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
wb.write(response.getOutputStream());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("多Sheet导出Excel异常{}", e.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(wb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多 Sheet 导出 —— 将多个不同类型的数据集合写入同一 Excel,生成文件并返回下载地址
|
||||
*
|
||||
* @param sheets Sheet 描述列表
|
||||
* @return AjaxResult(含文件下载地址)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static AjaxResult exportMultiSheet(List<ExcelSheet<?>> sheets)
|
||||
{
|
||||
if (sheets == null || sheets.isEmpty())
|
||||
{
|
||||
return AjaxResult.error("导出数据不能为空");
|
||||
}
|
||||
SXSSFWorkbook wb = buildWorkbook(sheets);
|
||||
OutputStream out = null;
|
||||
try
|
||||
{
|
||||
ExcelUtil firstUtil = new ExcelUtil(sheets.get(0).getClazz());
|
||||
String filename = firstUtil.encodingFilename(sheets.get(0).getSheetName());
|
||||
out = new FileOutputStream(firstUtil.getAbsoluteFile(filename));
|
||||
wb.write(out);
|
||||
return AjaxResult.success(filename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("多Sheet导出Excel异常{}", e.getMessage());
|
||||
throw new UtilException("导出Excel失败,请联系网站管理员!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(wb);
|
||||
IOUtils.closeQuietly(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建多 Sheet Workbook —— 创建 SXSSFWorkbook 并将所有 Sheet 数据写入
|
||||
*
|
||||
* @param sheets Sheet 描述列表
|
||||
* @return 已写入所有 Sheet 数据的 SXSSFWorkbook
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static SXSSFWorkbook buildWorkbook(List<ExcelSheet<?>> sheets)
|
||||
{
|
||||
SXSSFWorkbook wb = new SXSSFWorkbook(500);
|
||||
for (ExcelSheet<?> excelSheet : sheets)
|
||||
{
|
||||
ExcelUtil util = new ExcelUtil(excelSheet.getClazz());
|
||||
util.initWithWorkbook(wb, excelSheet.getList(), excelSheet.getSheetName(), excelSheet.getTitle());
|
||||
util.writeSheet();
|
||||
}
|
||||
return wb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用外部传入的 Workbook 初始化(多 Sheet 导出专用)
|
||||
* 与 init() 的区别:不新建 Workbook,而是在已有 wb 上追加新 Sheet
|
||||
*
|
||||
* @param wb 已有工作簿
|
||||
* @param list 数据集合
|
||||
* @param sheetName Sheet 名称
|
||||
* @param title 大标题(可为空)
|
||||
*/
|
||||
public void initWithWorkbook(SXSSFWorkbook wb, List<T> list, String sheetName, String title)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<T>();
|
||||
}
|
||||
this.list = list;
|
||||
this.sheetName = sheetName;
|
||||
this.title = title != null ? title : "";
|
||||
this.type = Type.EXPORT;
|
||||
this.rownum = 0;
|
||||
this.wb = wb;
|
||||
this.sheet = wb.createSheet(sheetName);
|
||||
createExcelField();
|
||||
this.styles = createStyles(wb);
|
||||
createTitle();
|
||||
createSubHead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对list数据源将其里面的数据导入到excel表单
|
||||
*
|
||||
@@ -1124,7 +1240,6 @@ public class ExcelUtil<T>
|
||||
/**
|
||||
* 添加单元格
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
|
||||
{
|
||||
Cell cell = null;
|
||||
@@ -1202,9 +1317,16 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
private CellStyle createCellStyle(CellStyle cellStyle, String format)
|
||||
{
|
||||
String key = cellStyle.getIndex() + "|" + format;
|
||||
CellStyle cached = cellStyleCache.get(key);
|
||||
if (cached != null)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
CellStyle style = wb.createCellStyle();
|
||||
style.cloneStyleFrom(cellStyle);
|
||||
style.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(format));
|
||||
cellStyleCache.put(key, style);
|
||||
return style;
|
||||
}
|
||||
|
||||
@@ -1426,7 +1548,7 @@ public class ExcelUtil<T>
|
||||
{
|
||||
try
|
||||
{
|
||||
Object instance = excel.handler().getDeclaredConstructor().newInstance();
|
||||
Object instance = excel.handler().newInstance();
|
||||
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
|
||||
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
|
||||
}
|
||||
|
||||
@@ -310,7 +310,6 @@ public class ReflectUtils
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void makeAccessible(Method method)
|
||||
{
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
@@ -323,7 +322,6 @@ public class ReflectUtils
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void makeAccessible(Field field)
|
||||
{
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SqlUtil
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
public static String SQL_REGEX = "\u000B|%0A|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
@@ -58,12 +58,13 @@ public class SqlUtil
|
||||
{
|
||||
return;
|
||||
}
|
||||
String normalizedValue = value.replaceAll("\\p{Z}|\\s", "");
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords)
|
||||
{
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
|
||||
if (StringUtils.indexOfIgnoreCase(normalizedValue, sqlKeyword) > -1)
|
||||
{
|
||||
throw new UtilException("参数存在SQL注入风险");
|
||||
throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ruoyi.common.xss;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.ruoyi.common.xss;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InMemoryCacheStoreTest
|
||||
{
|
||||
@Test
|
||||
void shouldExpireEntryAfterTtl() throws Exception
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("captcha_codes:1", "1234", 20, TimeUnit.MILLISECONDS);
|
||||
Thread.sleep(40);
|
||||
assertNull(store.get("captcha_codes:1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnPrefixKeysInSortedOrder()
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("login_tokens:a", "A");
|
||||
store.set("login_tokens:b", "B");
|
||||
store.set("sys_dict:x", "X");
|
||||
assertEquals(Set.of("login_tokens:a", "login_tokens:b"), store.keys("login_tokens:*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTrackHitsMissesWritesAndExpiredEntries() throws Exception
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("captcha_codes:2", "5678", 20, TimeUnit.MILLISECONDS);
|
||||
assertTrue(store.hasKey("captcha_codes:2"));
|
||||
assertEquals("5678", store.get("captcha_codes:2"));
|
||||
Thread.sleep(40);
|
||||
assertNull(store.get("captcha_codes:2"));
|
||||
assertFalse(store.hasKey("captcha_codes:2"));
|
||||
|
||||
InMemoryCacheStats stats = store.snapshot();
|
||||
assertEquals("IN_MEMORY", stats.cacheType());
|
||||
assertEquals("single-instance", stats.mode());
|
||||
assertEquals(0, stats.keySize());
|
||||
assertEquals(2, stats.hitCount());
|
||||
assertEquals(2, stats.missCount());
|
||||
assertEquals(1, stats.expiredCount());
|
||||
assertEquals(1, stats.writeCount());
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RedisCacheTest
|
||||
{
|
||||
@Test
|
||||
void shouldSupportSetGetDeleteAndExpireThroughRedisCacheFacade()
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
cache.setCacheObject("login_tokens:abc", "payload", 1, TimeUnit.SECONDS);
|
||||
assertEquals("payload", cache.getCacheObject("login_tokens:abc"));
|
||||
assertTrue(cache.hasKey("login_tokens:abc"));
|
||||
assertTrue(cache.deleteObject("login_tokens:abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportKeysBulkDeleteAndRemainingTtl()
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
cache.setCacheObject("login_tokens:a", "A", 5, TimeUnit.SECONDS);
|
||||
cache.setCacheObject("login_tokens:b", "B", 5, TimeUnit.SECONDS);
|
||||
cache.setCacheObject("sys_dict:x", "X");
|
||||
|
||||
assertNotNull(cache.getExpire("login_tokens:a"));
|
||||
assertTrue(cache.getExpire("login_tokens:a") > 0);
|
||||
assertEquals(Set.of("login_tokens:a", "login_tokens:b"), Set.copyOf(cache.keys("login_tokens:*")));
|
||||
assertTrue(cache.deleteObject(List.of("login_tokens:a", "login_tokens:b")));
|
||||
assertFalse(cache.hasKey("login_tokens:a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIncrementCounterWithinTtlWindow() throws Exception
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
assertEquals(1L, cache.increment("rate_limit:test", 50, TimeUnit.MILLISECONDS));
|
||||
assertEquals(2L, cache.increment("rate_limit:test", 50, TimeUnit.MILLISECONDS));
|
||||
Thread.sleep(70);
|
||||
assertNull(cache.getCacheObject("rate_limit:test"));
|
||||
assertEquals(1L, cache.increment("rate_limit:test", 50, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
|
||||
class DictUtilsTest
|
||||
{
|
||||
@AfterEach
|
||||
void clearSpringUtils()
|
||||
{
|
||||
ReflectionTestUtils.setField(SpringUtils.class, "beanFactory", null);
|
||||
ReflectionTestUtils.setField(SpringUtils.class, "applicationContext", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReadDictListFromLocalCache()
|
||||
{
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("redisCache", new RedisCache(new InMemoryCacheStore()));
|
||||
ReflectionTestUtils.setField(SpringUtils.class, "beanFactory", beanFactory);
|
||||
|
||||
SysDictData dictData = new SysDictData();
|
||||
dictData.setDictLabel("正常");
|
||||
dictData.setDictValue("0");
|
||||
|
||||
DictUtils.setDictCache("sys_normal_disable", List.of(dictData));
|
||||
|
||||
List<SysDictData> cached = DictUtils.getDictCache("sys_normal_disable");
|
||||
assertEquals(1, cached.size());
|
||||
assertEquals("正常", cached.get(0).getDictLabel());
|
||||
|
||||
DictUtils.clearDictCache();
|
||||
assertNull(DictUtils.getDictCache("sys_normal_disable"));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>3.9.1</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<!-- 阿里数据库连接池 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码 -->
|
||||
@@ -59,12 +59,6 @@
|
||||
<artifactId>ruoyi-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.annotation.DataScope;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
@@ -26,31 +27,6 @@ import com.ruoyi.framework.security.context.PermissionContextHolder;
|
||||
@Component
|
||||
public class DataScopeAspect
|
||||
{
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
|
||||
/**
|
||||
* 部门数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT = "3";
|
||||
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_SELF = "5";
|
||||
|
||||
/**
|
||||
* 数据权限过滤关键字
|
||||
*/
|
||||
@@ -74,7 +50,7 @@ public class DataScopeAspect
|
||||
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
|
||||
{
|
||||
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.userAlias(), controllerDataScope.deptAlias(), controllerDataScope.userField(), controllerDataScope.deptField(), permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,13 +64,13 @@ public class DataScopeAspect
|
||||
* @param userAlias 用户别名
|
||||
* @param permission 权限字符
|
||||
*/
|
||||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
|
||||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String userAlias, String deptAlias, String userField, String deptField, String permission)
|
||||
{
|
||||
StringBuilder sqlString = new StringBuilder();
|
||||
List<String> conditions = new ArrayList<String>();
|
||||
List<String> scopeCustomIds = new ArrayList<String>();
|
||||
user.getRoles().forEach(role -> {
|
||||
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))))
|
||||
if (Constants.Dept.DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && (StringUtils.isEmpty(permission) || StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))))
|
||||
{
|
||||
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
|
||||
}
|
||||
@@ -111,42 +87,42 @@ public class DataScopeAspect
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (DATA_SCOPE_ALL.equals(dataScope))
|
||||
if (Constants.Dept.DATA_SCOPE_ALL.equals(dataScope))
|
||||
{
|
||||
sqlString = new StringBuilder();
|
||||
conditions.add(dataScope);
|
||||
break;
|
||||
}
|
||||
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
|
||||
else if (Constants.Dept.DATA_SCOPE_CUSTOM.equals(dataScope))
|
||||
{
|
||||
if (scopeCustomIds.size() > 1)
|
||||
{
|
||||
// 多个自定数据权限使用in查询,避免多次拼接。
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, deptField, String.join(",", scopeCustomIds)));
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, deptField, role.getRoleId()));
|
||||
}
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
||||
else if (Constants.Dept.DATA_SCOPE_DEPT.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} = {} ", deptAlias, deptField, user.getDeptId()));
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||
else if (Constants.Dept.DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, deptField, user.getDeptId(), user.getDeptId()));
|
||||
}
|
||||
else if (DATA_SCOPE_SELF.equals(dataScope))
|
||||
else if (Constants.Dept.DATA_SCOPE_SELF.equals(dataScope))
|
||||
{
|
||||
if (StringUtils.isNotBlank(userAlias))
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} = {} ", userAlias, userField, user.getUserId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} = 0 ", deptAlias, deptField));
|
||||
}
|
||||
}
|
||||
conditions.add(dataScope);
|
||||
@@ -155,7 +131,7 @@ public class DataScopeAspect
|
||||
// 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
|
||||
if (StringUtils.isEmpty(conditions))
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||
sqlString.append(StringUtils.format(" OR {}.{} = 0 ", deptAlias, deptField));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(sqlString.toString()))
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.ruoyi.framework.aspectj;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.ruoyi.framework.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.annotation.RateLimiter;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.enums.LimitType;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
|
||||
/**
|
||||
@@ -26,11 +30,20 @@ public class RateLimiterAspect
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
|
||||
|
||||
private final RedisCache redisCache;
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
public RateLimiterAspect(RedisCache redisCache)
|
||||
private RedisScript<Long> limitScript;
|
||||
|
||||
@Autowired
|
||||
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
|
||||
{
|
||||
this.redisCache = redisCache;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setLimitScript(RedisScript<Long> limitScript)
|
||||
{
|
||||
this.limitScript = limitScript;
|
||||
}
|
||||
|
||||
@Before("@annotation(rateLimiter)")
|
||||
@@ -40,14 +53,15 @@ public class RateLimiterAspect
|
||||
int count = rateLimiter.count();
|
||||
|
||||
String combineKey = getCombineKey(rateLimiter, point);
|
||||
List<Object> keys = Collections.singletonList(combineKey);
|
||||
try
|
||||
{
|
||||
long number = redisCache.increment(combineKey, time, TimeUnit.SECONDS);
|
||||
if (number > count)
|
||||
Long number = redisTemplate.execute(limitScript, keys, count, time);
|
||||
if (StringUtils.isNull(number) || number.intValue() > count)
|
||||
{
|
||||
throw new ServiceException("访问过于频繁,请稍候再试");
|
||||
}
|
||||
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number, combineKey);
|
||||
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,11 @@ package com.ruoyi.framework.config;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -11,18 +16,13 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
|
||||
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import com.ruoyi.common.enums.DataSourceType;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.framework.config.properties.DruidProperties;
|
||||
import com.ruoyi.framework.datasource.DynamicDataSource;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
|
||||
/**
|
||||
* druid 配置多数据源
|
||||
@@ -96,7 +96,7 @@ public class DruidConfig
|
||||
Filter filter = new Filter()
|
||||
{
|
||||
@Override
|
||||
public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException
|
||||
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
}
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.SerializationException;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.filter.Filter;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
|
||||
/**
|
||||
* Redis使用FastJson序列化
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
|
||||
{
|
||||
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
|
||||
|
||||
private Class<T> clazz;
|
||||
|
||||
public FastJson2JsonRedisSerializer(Class<T> clazz)
|
||||
{
|
||||
super();
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(T t) throws SerializationException
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(byte[] bytes) throws SerializationException
|
||||
{
|
||||
if (bytes == null || bytes.length <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String str = new String(bytes, DEFAULT_CHARSET);
|
||||
|
||||
return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user